Monday, April 6, 2020

Improving XML Decryption performance

Apache Santuario - XML Security for Java is a library that provides the ability to sign and encrypt XML. In this post we'll focus solely on encryption. Apache Santuario has two APIs for XML Encryption, an in-memory (DOM) based approach and a streaming (StAX based) API. The streaming API offers very good memory performance as the size of the XML being encrypted scales up. However there is not much difference in speed, particularly for decryption. The streaming API is also less flexible than the DOM API. In this post we will look at different ways to speed up the DOM based API using Serializers.

1) Serializers in Apache Santuario

The XMLCipher class in Santuario is the main entry point for encryption and decryption. The XMLCipher class makes use of the Serializer interface to serialize DOM elements to byte arrays for encryption, and to deserialize byte arrays to DOM elements for decryption. Santuario ships with two different implementations. The default is TransformSerializer, which makes use of the "javax.xml.transform.Transformer" API and requires Apache Xalan to work properly. The other alternative is the DocumentSerializer, which uses the standard DOM API. These implementations perform very similarly.

Apache CXF is a web services framework that heavily leverages Apache Santuario to perform XML encryption and decryption of web service messages. CXF is fully streaming based and so it made sense to look to re-use some of this functionality with a custom Serializer implementation. The result is the StaxSerializer. This largely re-uses the DOM implementation for encryption, but uses a streaming based approach for deserialization. StaxSerializer is used by default in Apache CXF when working with XML encryption/decryption.

2) Benchmarking

To benchmark the StaxSerializer, I adapted the Santuario benchmarking test-suite just to compare encryption performance using the different Serializers, and put it into github here:
  • santuario-serializer-benchmark: This project contains two Junit tests used for benchmarking XML Encryption. In particular, they measure memory and timing performance for both encryption and decryption, ranging from small to very large XML files.
a) Heap memory consumption during decryption

Here is the result for the (default) TransformSerializer:

 In comparison, here is the result for the StaxSerializer:
The most obvious conclusion is that the streaming API is far more efficient in terms of memory consumption, especially when we scale to larger documents. However one would expect also to see less memory being consumed using the StaxSerializer compared to the TransformSerializer. Indeed, one can see that the TransformSerializer almost consumes 600MB for the largest case, wheras the StaxSerializer comes in under 450MB.

b) Time needed for decryption

Turning now to execution time, here is the result for the (default) TransformSerializer:

As stated above, the performance is fairly identical to the streaming layer. This is because the streaming implementation needs to make several passes of the XML for consistency reasons. Here is the result for the StaxSerializer:
Here we can see that the StaxSerializer actually offers superior performance to the streaming API. So if XML decryption performance is an issue for you, it might be worth considering using CXF's StaxSerializer.