Various cleanups to AMQP and Kryo serialisation:

* PartyAndCertificate serialiser has been converted to deal with just CertPath
* Removed X500Name serialiser as we no longer use the BC implementation and have our own CordaX500Name
* Converted X509 cert serialiser to use Java certs and not BC X509 cert holder
* Added to test to AMQP serialisation to make sure c'tor is invoked on deserialisation
This commit is contained in:
Shams Asari 2017-11-20 11:57:58 +00:00
parent 288eb5fcc4
commit f7e51a9ae1
12 changed files with 166 additions and 169 deletions

View File

@ -0,0 +1,13 @@
package net.corda.core.contracts
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.Test
class PrivacySaltTest {
@Test
fun `all-zero PrivacySalt not allowed`() {
assertThatExceptionOfType(IllegalArgumentException::class.java).isThrownBy {
PrivacySalt(ByteArray(32))
}.withMessage("Privacy salt should not be all zeros.")
}
}

View File

@ -36,7 +36,6 @@ abstract class AbstractAMQPSerializationScheme : SerializationScheme {
register(publicKeySerializer)
register(net.corda.nodeapi.internal.serialization.amqp.custom.PrivateKeySerializer)
register(net.corda.nodeapi.internal.serialization.amqp.custom.ThrowableSerializer(this))
register(net.corda.nodeapi.internal.serialization.amqp.custom.X500NameSerializer)
register(net.corda.nodeapi.internal.serialization.amqp.custom.BigDecimalSerializer)
register(net.corda.nodeapi.internal.serialization.amqp.custom.CurrencySerializer)
register(net.corda.nodeapi.internal.serialization.amqp.custom.OpaqueBytesSubSequenceSerializer(this))
@ -54,8 +53,8 @@ abstract class AbstractAMQPSerializationScheme : SerializationScheme {
register(net.corda.nodeapi.internal.serialization.amqp.custom.MonthDaySerializer(this))
register(net.corda.nodeapi.internal.serialization.amqp.custom.PeriodSerializer(this))
register(net.corda.nodeapi.internal.serialization.amqp.custom.ClassSerializer(this))
register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateHolderSerializer)
register(net.corda.nodeapi.internal.serialization.amqp.custom.PartyAndCertificateSerializer(factory))
register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateSerializer)
register(net.corda.nodeapi.internal.serialization.amqp.custom.CertPathSerializer(this))
register(net.corda.nodeapi.internal.serialization.amqp.custom.StringBufferSerializer)
register(net.corda.nodeapi.internal.serialization.amqp.custom.SimpleStringSerializer)
register(net.corda.nodeapi.internal.serialization.amqp.custom.InputStreamSerializer)

View File

@ -0,0 +1,27 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import java.io.NotSerializableException
import java.security.cert.CertPath
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
class CertPathSerializer(factory: SerializerFactory)
: CustomSerializer.Proxy<CertPath, CertPathSerializer.CertPathProxy>(CertPath::class.java, CertPathProxy::class.java, factory) {
override fun toProxy(obj: CertPath): CertPathProxy = CertPathProxy(obj.type, obj.encoded)
override fun fromProxy(proxy: CertPathProxy): CertPath {
try {
val cf = CertificateFactory.getInstance(proxy.type)
return cf.generateCertPath(proxy.encoded.inputStream())
} catch (ce: CertificateException) {
val nse = NotSerializableException("java.security.cert.CertPath: $type")
nse.initCause(ce)
throw nse
}
}
@Suppress("ArrayInDataClass")
data class CertPathProxy(val type: String, val encoded: ByteArray)
}

View File

@ -1,30 +0,0 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.core.crypto.Crypto
import net.corda.core.identity.PartyAndCertificate
import net.corda.nodeapi.internal.serialization.amqp.*
import java.io.ByteArrayInputStream
import java.io.NotSerializableException
import java.security.cert.CertPath
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
/**
* A serializer that writes out a party and certificate in encoded format.
*/
class PartyAndCertificateSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<PartyAndCertificate, PartyAndCertificateSerializer.PartyAndCertificateProxy>(PartyAndCertificate::class.java, PartyAndCertificateProxy::class.java, factory) {
override fun toProxy(obj: PartyAndCertificate): PartyAndCertificateProxy = PartyAndCertificateProxy(obj.certPath.type, obj.certPath.encoded)
override fun fromProxy(proxy: PartyAndCertificateProxy): PartyAndCertificate {
try {
val cf = CertificateFactory.getInstance(proxy.type)
return PartyAndCertificate(cf.generateCertPath(ByteArrayInputStream(proxy.encoded)))
} catch (ce: CertificateException) {
val nse = NotSerializableException("java.security.cert.CertPath: " + type)
nse.initCause(ce)
throw nse
}
}
data class PartyAndCertificateProxy(val type: String, val encoded: ByteArray)
}

View File

@ -1,23 +0,0 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.nodeapi.internal.serialization.amqp.*
import org.apache.qpid.proton.codec.Data
import org.bouncycastle.asn1.ASN1InputStream
import org.bouncycastle.asn1.x500.X500Name
import java.lang.reflect.Type
/**
* Custom serializer for X500 names that utilizes their ASN.1 encoding on the wire.
*/
object X500NameSerializer : CustomSerializer.Implements<X500Name>(X500Name::class.java) {
override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), SerializerFactory.primitiveTypeName(ByteArray::class.java)!!, descriptor, emptyList())))
override fun writeDescribedObject(obj: X500Name, data: Data, type: Type, output: SerializationOutput) {
output.writeObject(obj.encoded, data, clazz)
}
override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): X500Name {
val binary = input.readObject(obj, schema, ByteArray::class.java) as ByteArray
return X500Name.getInstance(ASN1InputStream(binary).readObject())
}
}

View File

@ -1,23 +0,0 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.core.crypto.Crypto
import net.corda.nodeapi.internal.serialization.amqp.*
import org.apache.qpid.proton.codec.Data
import org.bouncycastle.cert.X509CertificateHolder
import java.lang.reflect.Type
/**
* A serializer that writes out a certificate in X.509 format.
*/
object X509CertificateHolderSerializer : CustomSerializer.Implements<X509CertificateHolder>(X509CertificateHolder::class.java) {
override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), SerializerFactory.primitiveTypeName(ByteArray::class.java)!!, descriptor, emptyList())))
override fun writeDescribedObject(obj: X509CertificateHolder, data: Data, type: Type, output: SerializationOutput) {
output.writeObject(obj.encoded, data, clazz)
}
override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): X509CertificateHolder {
val bits = input.readObject(obj, schema, ByteArray::class.java) as ByteArray
return X509CertificateHolder(bits)
}
}

View File

@ -0,0 +1,27 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.nodeapi.internal.serialization.amqp.*
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
object X509CertificateSerializer : CustomSerializer.Implements<X509Certificate>(X509Certificate::class.java) {
override val schemaForDocumentation = Schema(listOf(RestrictedType(
type.toString(),
"",
listOf(type.toString()),
SerializerFactory.primitiveTypeName(ByteArray::class.java)!!,
descriptor,
emptyList()
)))
override fun writeDescribedObject(obj: X509Certificate, data: Data, type: Type, output: SerializationOutput) {
output.writeObject(obj.encoded, data, clazz)
}
override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): X509Certificate {
val bits = input.readObject(obj, schema, ByteArray::class.java) as ByteArray
return CertificateFactory.getInstance("X.509").generateCertificate(bits.inputStream()) as X509Certificate
}
}

View File

@ -32,8 +32,6 @@ import net.corda.nodeapi.internal.serialization.GeneratedAttachment
import net.corda.nodeapi.internal.serialization.MutableClassWhitelist
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateCrtKey
@ -53,6 +51,7 @@ import java.io.InputStream
import java.lang.reflect.Modifier.isPublic
import java.security.PublicKey
import java.security.cert.CertPath
import java.security.cert.X509Certificate
import java.util.*
import kotlin.collections.ArrayList
@ -109,8 +108,7 @@ object DefaultKryoCustomizer {
register(FileInputStream::class.java, InputStreamSerializer)
register(CertPath::class.java, CertPathSerializer)
register(X509CertPath::class.java, CertPathSerializer)
register(X500Name::class.java, X500NameSerializer)
register(X509CertificateHolder::class.java, X509CertificateSerializer)
register(X509Certificate::class.java, X509CertificateSerializer)
register(BCECPrivateKey::class.java, PrivateKeySerializer)
register(BCECPublicKey::class.java, publicKeySerializer)
register(BCRSAPrivateCrtKey::class.java, PrivateKeySerializer)

View File

@ -15,7 +15,8 @@ import net.corda.core.crypto.TransactionSignature
import net.corda.core.identity.Party
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationContext.UseCase.*
import net.corda.core.serialization.SerializationContext.UseCase.Checkpoint
import net.corda.core.serialization.SerializationContext.UseCase.Storage
import net.corda.core.serialization.SerializeAsTokenContext
import net.corda.core.serialization.SerializedBytes
import net.corda.core.toFuture
@ -23,9 +24,6 @@ import net.corda.core.toObservable
import net.corda.core.transactions.*
import net.corda.nodeapi.internal.serialization.CordaClassResolver
import net.corda.nodeapi.internal.serialization.serializationContextKey
import org.bouncycastle.asn1.ASN1InputStream
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import rx.Observable
@ -36,6 +34,7 @@ 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.util.*
import javax.annotation.concurrent.ThreadSafe
import kotlin.reflect.KClass
@ -471,46 +470,28 @@ object ClassSerializer : Serializer<Class<*>>() {
}
}
/**
* For serialising an [X500Name] without touching Sun internal classes.
*/
@ThreadSafe
object X500NameSerializer : Serializer<X500Name>() {
override fun read(kryo: Kryo, input: Input, type: Class<X500Name>): X500Name {
return X500Name.getInstance(ASN1InputStream(input.readBytes()).readObject())
}
override fun write(kryo: Kryo, output: Output, obj: X500Name) {
output.writeBytes(obj.encoded)
}
}
/**
* For serialising an [CertPath] in an X.500 standard format.
*/
@ThreadSafe
object CertPathSerializer : Serializer<CertPath>() {
val factory: CertificateFactory = CertificateFactory.getInstance("X.509")
override fun read(kryo: Kryo, input: Input, type: Class<CertPath>): CertPath {
return factory.generateCertPath(input)
val factory = CertificateFactory.getInstance(input.readString())
return factory.generateCertPath(input.readBytesWithLength().inputStream())
}
override fun write(kryo: Kryo, output: Output, obj: CertPath) {
output.writeBytes(obj.encoded)
output.writeString(obj.type)
output.writeBytesWithLength(obj.encoded)
}
}
/**
* For serialising an [X509CertificateHolder] in an X.500 standard format.
*/
@ThreadSafe
object X509CertificateSerializer : Serializer<X509CertificateHolder>() {
override fun read(kryo: Kryo, input: Input, type: Class<X509CertificateHolder>): X509CertificateHolder {
return X509CertificateHolder(input.readBytes())
object X509CertificateSerializer : Serializer<X509Certificate>() {
override fun read(kryo: Kryo, input: Input, type: Class<X509Certificate>): X509Certificate {
val factory = CertificateFactory.getInstance("X.509")
return factory.generateCertificate(input.readBytesWithLength().inputStream()) as X509Certificate
}
override fun write(kryo: Kryo, output: Output, obj: X509CertificateHolder) {
output.writeBytes(obj.encoded)
override fun write(kryo: Kryo, output: Output, obj: X509Certificate) {
output.writeBytesWithLength(obj.encoded)
}
}

View File

@ -21,13 +21,14 @@ import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.time.Instant
import java.util.Collections
import kotlin.test.*
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class KryoTests {
@Rule
@ -36,9 +37,6 @@ class KryoTests {
private lateinit var factory: SerializationFactory
private lateinit var context: SerializationContext
@get:Rule
val expectedEx: ExpectedException = ExpectedException.none()
@Before
fun setup() {
factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) }
@ -51,7 +49,7 @@ class KryoTests {
}
@Test
fun ok() {
fun `simple data class`() {
val birthday = Instant.parse("1984-04-17T00:30:00.00Z")
val mike = Person("mike", birthday)
val bits = mike.serialize(factory, context)
@ -59,7 +57,7 @@ class KryoTests {
}
@Test
fun nullables() {
fun `null values`() {
val bob = Person("bob", null)
val bits = bob.serialize(factory, context)
assertThat(bits.deserialize(factory, context)).isEqualTo(Person("bob", null))
@ -202,13 +200,6 @@ class KryoTests {
assertEquals(expected, actual)
}
@Test
fun `all-zero PrivacySalt not allowed`() {
expectedEx.expect(IllegalArgumentException::class.java)
expectedEx.expectMessage("Privacy salt should not be all zeros.")
PrivacySalt(ByteArray(32))
}
@CordaSerializable
private object TestSingleton

View File

@ -2,16 +2,16 @@ package net.corda.nodeapi.internal.serialization
import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.util.DefaultClassResolver
import net.corda.core.identity.CordaX500Name
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.node.services.statemachine.SessionData
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.amqpSpecific
import net.corda.testing.kryoSpecific
import net.corda.testing.SerializationEnvironmentRule
import org.assertj.core.api.Assertions
import org.bouncycastle.asn1.x500.X500Name
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Assert.assertArrayEquals
import org.junit.Rule
import org.junit.Test
@ -53,7 +53,7 @@ class MapsSerializationTest {
fun `check throws for forbidden declared type`() = amqpSpecific("Such exceptions are not expected in Kryo mode.") {
val payload = HashMap<String, String>(smallMap)
val wrongPayloadType = WrongPayloadType(payload)
Assertions.assertThatThrownBy { wrongPayloadType.serialize() }
assertThatThrownBy { wrongPayloadType.serialize() }
.isInstanceOf(IllegalArgumentException::class.java).hasMessageContaining(
"Map type class java.util.HashMap is unstable under iteration. Suggested fix: use java.util.LinkedHashMap instead.")
}
@ -62,27 +62,29 @@ class MapsSerializationTest {
data class MyKey(val keyContent: Double)
@CordaSerializable
data class MyValue(val valueContent: X500Name)
data class MyValue(val valueContent: CordaX500Name)
@Test
fun `check map serialization works with custom types`() {
val myMap = mapOf(
MyKey(1.0) to MyValue(X500Name("CN=one")),
MyKey(10.0) to MyValue(X500Name("CN=ten")))
MyKey(1.0) to MyValue(CordaX500Name("OOO", "LLL", "CC")),
MyKey(10.0) to MyValue(CordaX500Name("OO", "LL", "CC")))
assertEqualAfterRoundTripSerialization(myMap)
}
@Test
fun `check empty map serialises as Java emptyMap`() = kryoSpecific("Specifically checks Kryo serialization") {
val nameID = 0
val serializedForm = emptyMap<Int, Int>().serialize()
val output = ByteArrayOutputStream().apply {
write(KryoHeaderV0_1.bytes)
write(DefaultClassResolver.NAME + 2)
write(nameID)
write(javaEmptyMapClass.name.toAscii())
write(Kryo.NOT_NULL.toInt())
fun `check empty map serialises as Java emptyMap`() {
kryoSpecific("Specifically checks Kryo serialization") {
val nameID = 0
val serializedForm = emptyMap<Int, Int>().serialize()
val output = ByteArrayOutputStream().apply {
write(KryoHeaderV0_1.bytes)
write(DefaultClassResolver.NAME + 2)
write(nameID)
write(javaEmptyMapClass.name.toAscii())
write(Kryo.NOT_NULL.toInt())
}
assertArrayEquals(output.toByteArray(), serializedForm.bytes)
}
assertArrayEquals(output.toByteArray(), serializedForm.bytes)
}
}

View File

@ -1,17 +1,20 @@
@file:Suppress("unused", "MemberVisibilityCanPrivate")
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.client.rpc.RPCException
import net.corda.core.CordaRuntimeException
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.secureRandomBytes
import net.corda.core.flows.FlowException
import net.corda.core.identity.AbstractParty
import net.corda.core.internal.toX509CertHolder
import net.corda.core.internal.AbstractAttachment
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.MissingAttachmentsException
import net.corda.core.serialization.SerializationFactory
import net.corda.core.transactions.LedgerTransaction
import net.corda.client.rpc.RPCException
import net.corda.core.contracts.*
import net.corda.core.internal.AbstractAttachment
import net.corda.core.serialization.MissingAttachmentsException
import net.corda.core.utilities.OpaqueBytes
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.EmptyWhitelist
import net.corda.nodeapi.internal.serialization.GeneratedAttachment
@ -25,10 +28,8 @@ import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.qpid.proton.amqp.*
import org.apache.qpid.proton.codec.DecoderImpl
import org.apache.qpid.proton.codec.EncoderImpl
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertNotSame
import org.junit.Assert.assertSame
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.assertj.core.api.Assertions.*
import org.junit.Assert.*
import org.junit.Ignore
import org.junit.Test
import java.io.ByteArrayInputStream
@ -39,10 +40,7 @@ import java.nio.ByteBuffer
import java.time.*
import java.time.temporal.ChronoUnit
import java.util.*
import kotlin.reflect.full.declaredFunctions
import kotlin.reflect.full.declaredMemberFunctions
import kotlin.reflect.full.superclasses
import kotlin.reflect.jvm.javaMethod
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@ -79,7 +77,6 @@ class SerializationOutputTests {
}
data class Woo(val fred: Int) {
@Suppress("unused")
val bob = "Bob"
}
@ -89,7 +86,6 @@ class SerializationOutputTests {
@CordaSerializable
data class AnnotatedWoo(val fred: Int) {
@Suppress("unused")
val bob = "Bob"
}
@ -151,6 +147,13 @@ class SerializationOutputTests {
data class PolymorphicProperty(val foo: FooInterface?)
@CordaSerializable
class NonZeroByte(val value: Byte) {
init {
require(value.toInt() != 0) { "Zero not allowed" }
}
}
private inline fun <reified T : Any> serdes(obj: T,
factory: SerializerFactory = SerializerFactory(
AllWhitelist, ClassLoader.getSystemClassLoader()),
@ -406,6 +409,32 @@ class SerializationOutputTests {
serdes(obj)
}
@Test
fun `class constructor is invoked on deserialisation`() {
val ser = SerializationOutput(SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()))
val des = DeserializationInput(ser.serializerFactory)
val serialisedOne = ser.serialize(NonZeroByte(1)).bytes
val serialisedTwo = ser.serialize(NonZeroByte(2)).bytes
// Find the index that holds the value byte
val valueIndex = serialisedOne.zip(serialisedTwo).mapIndexedNotNull { index, (oneByte, twoByte) ->
if (oneByte.toInt() == 1 && twoByte.toInt() == 2) index else null
}.single()
val copy = serialisedTwo.clone()
// Double check
copy[valueIndex] = 0x03
assertThat(des.deserialize(OpaqueBytes(copy), NonZeroByte::class.java).value).isEqualTo(3)
// Now use the forbidden value
copy[valueIndex] = 0x00
assertThatExceptionOfType(NotSerializableException::class.java).isThrownBy {
des.deserialize(OpaqueBytes(copy), NonZeroByte::class.java)
}.withMessageContaining("Zero not allowed")
}
@Test
fun `test custom serializers on public key`() {
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
@ -762,26 +791,32 @@ class SerializationOutputTests {
}
@Test
fun `test certificate holder serialize`() {
fun `test privacy salt serialize`() {
serdes(PrivacySalt())
serdes(PrivacySalt(secureRandomBytes(32)))
}
@Test
fun `test X509 certificate serialize`() {
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateHolderSerializer)
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateSerializer)
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateHolderSerializer)
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateSerializer)
val obj = BOB_IDENTITY.certificate.toX509CertHolder()
val obj = BOB_IDENTITY.certificate
serdes(obj, factory, factory2)
}
@Test
fun `test party and certificate serialize`() {
fun `test cert path serialize`() {
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.PartyAndCertificateSerializer(factory))
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.CertPathSerializer(factory))
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.PartyAndCertificateSerializer(factory2))
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.CertPathSerializer(factory2))
val obj = BOB_IDENTITY
val obj = BOB_IDENTITY.certPath
serdes(obj, factory, factory2)
}