3

I am looking for an easy way calculate an HMAC over the ciphertext in Java (encrypt-then-MAC, including the IV and algorithm parameters) using InputStream/OutputStreams.

  • Failed attempt: I first tried inheriting from CipherOutputStream, but they do not allow retrieving the ciphertext programatically after after it has been written -- leading me to the conclusion that using CipherOutputStream only MAC-then-encrypt can be implemented. Code here: http://pastebin.com/armvDN3N

  • Successful attempt: I then used the OpenJDK7 source code of CipherOutputStream as a basis to implement a CipherHmacOutputStream class, that updates the Hmac whenever write() is called (mac.update()) and appends it to the stream when close is called. Code here: http://pastebin.com/fF8WFBpA

The problem now is when I try to write a matching CipherHmacInputStream, there is no obvious way to tell when the HMAC begins, because there is no way to tell when the stream ends. The available() method only returns the "new" decrypted bytes in the buffer. As a result, the decryption (cipher.update()) might wrongfully try to decrypt the HMAC. I already invested a bit of time in this, but there must be an easier way to do stream-based encryption/decryption with HMACs. GCM/EAX are the obvious answers, but I want to leave the algorithms/modes configurable, and also offer message authentication if GCM/EAX is not used.

Do you have any ideas? Are there any standard ways to do this?

binwiederhier
  • 1,893
  • 1
  • 16
  • 23
  • Regarding your first "failed" solution. You could have just add your `FilterOutputStream` between `CipherOutputStream` and `OutputStream` to catch cipher text. – Dmitry Zaytsev Mar 10 '14 at 14:19

1 Answers1

1

You're baking two pieces of data into a serialized form. You'll have to use some sort of header the stores the lengths so you can isolate each one later. While you're at it, add a version to the datagram so that, in the future, you can maintain backward compatibility.

David Ehrmann
  • 7,366
  • 2
  • 31
  • 40
  • The problem is that while a header with a length field is very useful, the length of the ciphertext is not known until the last byte is written. – binwiederhier Sep 14 '13 at 11:21
  • 1
    HTTP handles this with chunks. Write a one-byte header for each chunk of the data. If the size is zero, that means you've reached the end, e.g. 0xff <255 bytes> 0x10 <16 bytes> 0x00 – David Ehrmann Sep 14 '13 at 18:02