diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 167df1cddc..9c48115b0f 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -19,6 +19,8 @@ from the previous milestone release. * java.security.cert.CRLReason added to the default Whitelist. +* java.security.cert.X509CRL serialization support added. + .. _changelog_v3: Version 3.0 diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt index b56cbea372..96d01fa287 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt @@ -71,6 +71,7 @@ abstract class AbstractAMQPSerializationScheme(val cordappLoader: List) 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.X509CertificateSerializer) + register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CRLSerializer) 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) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CRLSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CRLSerializer.kt new file mode 100644 index 0000000000..2f98bddb74 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CRLSerializer.kt @@ -0,0 +1,27 @@ +package net.corda.nodeapi.internal.serialization.amqp.custom + +import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import net.corda.nodeapi.internal.serialization.amqp.* +import org.apache.qpid.proton.codec.Data +import java.lang.reflect.Type +import java.security.cert.X509CRL + +object X509CRLSerializer : CustomSerializer.Implements(X509CRL::class.java) { + override val schemaForDocumentation = Schema(listOf(RestrictedType( + type.toString(), + "", + listOf(type.toString()), + SerializerFactory.primitiveTypeName(ByteArray::class.java)!!, + descriptor, + emptyList() + ))) + + override fun writeDescribedObject(obj: X509CRL, data: Data, type: Type, output: SerializationOutput) { + output.writeObject(obj.encoded, data, clazz) + } + + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): X509CRL { + val bytes = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray + return X509CertificateFactory().delegate.generateCRL(bytes.inputStream()) as X509CRL + } +} \ No newline at end of file diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index bf081d04ad..9f2921e8f3 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -8,15 +8,19 @@ import net.corda.client.rpc.RPCException import net.corda.core.CordaException import net.corda.core.CordaRuntimeException import net.corda.core.contracts.* +import net.corda.core.crypto.Crypto 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.identity.CordaX500Name import net.corda.core.internal.AbstractAttachment +import net.corda.core.internal.x500Name import net.corda.core.serialization.* import net.corda.core.transactions.LedgerTransaction import net.corda.core.utilities.OpaqueBytes +import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA +import net.corda.nodeapi.internal.crypto.ContentSignerBuilder import net.corda.nodeapi.internal.serialization.* import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive import net.corda.testing.contracts.DummyContract @@ -29,6 +33,9 @@ import org.apache.qpid.proton.amqp.* import org.apache.qpid.proton.codec.DecoderImpl import org.apache.qpid.proton.codec.EncoderImpl import org.assertj.core.api.Assertions.* +import org.bouncycastle.cert.X509v2CRLBuilder +import org.bouncycastle.cert.jcajce.JcaX509CRLConverter +import org.bouncycastle.jce.provider.BouncyCastleProvider import org.junit.Assert.* import org.junit.Ignore import org.junit.Rule @@ -41,6 +48,7 @@ import java.io.IOException import java.io.NotSerializableException import java.math.BigDecimal import java.math.BigInteger +import java.security.cert.X509CRL import java.time.* import java.time.temporal.ChronoUnit import java.util.* @@ -51,6 +59,7 @@ import kotlin.test.assertTrue object AckWrapper { object Ack + fun serialize() { val factory = testDefaultFactoryNoEvolution() SerializationOutput(factory).serialize(Ack) @@ -59,6 +68,7 @@ object AckWrapper { object PrivateAckWrapper { private object Ack + fun serialize() { val factory = testDefaultFactoryNoEvolution() SerializationOutput(factory).serialize(Ack) @@ -630,6 +640,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi } private val FOO_PROGRAM_ID = "net.corda.nodeapi.internal.serialization.amqp.SerializationOutputTests.FooContract" + class FooState : ContractState { override val participants: List = emptyList() } @@ -992,6 +1003,25 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi assertEquals(objCopy.a, objCopy.b) } + private fun emptyCrl(): X509CRL { + val builder = X509v2CRLBuilder(CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.issuerX500Principal).x500Name, Date()) + val provider = BouncyCastleProvider() + val crlHolder = builder.build(ContentSignerBuilder.build(Crypto.RSA_SHA256, Crypto.generateKeyPair(Crypto.RSA_SHA256).private, provider)) + return JcaX509CRLConverter().setProvider(provider).getCRL(crlHolder) + } + + @Test + fun `test X509CRL custom serializer`() { + val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CRLSerializer) + + val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CRLSerializer) + + val obj = emptyCrl() + serdes(obj, factory, factory2) + } + data class ByteArrays(val a: ByteArray, val b: ByteArray) @Test @@ -1197,7 +1227,8 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi @Test fun throwable() { class TestException(message: String?, cause: Throwable?) : CordaException(message, cause) - val testExcp = TestException("hello", Throwable().apply { stackTrace = Thread.currentThread().stackTrace } ) + + val testExcp = TestException("hello", Throwable().apply { stackTrace = Thread.currentThread().stackTrace }) val factory = testDefaultFactoryNoEvolution() SerializationOutput(factory).serialize(testExcp)