Search code examples
javaapacheencryptionaesbouncycastle

Java's CipherOutputStream vs. Apache's CryptoOutputStream performance


I'm implementing AES256 encryption in my application, it's done via custom FilterOutputStream where I wrap provided output stream with an encrypted stream. Target encryption is AES/CTR/NoPadding.

At first, I started with using standard Java's javax.crypto.CipherOutputStream, so the code looked like that:

...
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, sessionKey, new IvParameterSpec(ivBytes));
this.out = new CipherOutputStream(out, cipher);

But I noticed that the encryption of 100 MB file takes ~10 minutes (yes, minutes!) with this approach, which is, obviously, not appropriate throughput.

So I started tinkering around and my first guess was to use Bouncy Castle as a Cipher provider (because it was recommended somewhere here, on SO). So the code became:

...
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", BouncyCastleProvider.PROVIDER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, sessionKey, new IvParameterSpec(ivBytes));
this.out = new CipherOutputStream(out, cipher);

With this change time of encryption decreased to ~45 seconds, which was more than 10 times better, but still inappropriate.

So what I've done next was replacing standard CipherOutputStream with the CryptoOutputStream, provided by Apache Commons Crypto, so the code started looking like that:

...
this.out = new CryptoOutputStream("AES/CTR/NoPadding", new Properties(), out, sessionKey, new IvParameterSpec(ivBytes));

And - voila - the encryptions now takes only ~2 seconds for the same file, so it goes about 15 times faster than BC's approach and more than 150x faster than the original approach!

So, my question is... What is the reason behind that? Is that just the fact that the default CipherOutputStream in Java is so bad and the Apache's CryptoOutputStream is so good? But then, how does Bouncy Castle boost the standard CipherOutputStream? What am I missing here?

UPD: I believe it's not a duplicate of Java I/O classes and performance, because that question was explicitly about the performance of plain streams, but this question is about the performance of encryption streams and the answer is not that obvious.


Solution

  • You need to wrap the stream in a BufferedOutputStream, this can make the IO more efficient because instead of many small writes, a single larger write is used.

    Depending on the exact use, it might be better to buffer the CipherOutputStream than the out. Sometimes it may help to wrap both. YMMV, I'd suggest to test what works best.