Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Buffering reads from a CipherInputStream

I'm running into a scenario where I need to buffer a CipherInputStream. To be exact, I need to ensure the buffer is filled to 32K or an EOF is reached before returing the results to the caller of InputStream.read(byte[], int, int). And here is where I am running into a bit of a snag that I'm hoping you all can help with.

  • Oracle JRE - CipherInputStream has an internal buffer of 512 bytes, for no particular reason.
  • IBM JRE - CipherInputStream has an internal buffer of 8KB

Simply wrapping the CipherInputStream with a BufferedInputStream doesn't give me any benefit because CipherInputStream.available() will return 0 which makes the buffering pointless. The javadoc of the CipherInputStream states that this should be overridden by the subclass http://docs.oracle.com/javase/7/docs/api/javax/crypto/CipherInputStream.html#available()

Since I am working on FileInputStream's (in most cases) as my lowest level stream, and my cipher algorithm is CTR with no Padding, I have the same number of bytes pre- and post- encryption, but this is where things start to get fuzzy for me. I've overridden the CipherInputStream.available() to return super.available() and this seems to work great for local files using Oracle's CipherInputStream.

I can ensure that my 32K buffer is filled with a call to read(byte[], int, int), or an end of file is reached.

Output using Oracle JRE

**CipherInputStream.available: 51121
**CipherInputStream.available: 50609
**CipherInputStream.available: 50097
...
**CipherInputStream.available: 19889
**CipherInputStream.available: 19377
**BufferedInputStream.read: 32768
**CipherInputStream.available: 18353
**CipherInputStream.available: 17841
**CipherInputStream.available: 17329
...
**CipherInputStream.available: 433
**CipherInputStream.available: 0
**BufferedInputStream.read: 18865
**EOF

However, on IBMs JVM, it's as if a call to in.available on the underlying FileInputStream is skipped, which is causing problems with an api that is expected to return a full buffer, or end of file. This is the same behavior using the IBM JRE on Windows and iSeries.

Output using IBM JRE

**CipherInputStream.available: 43441
**CipherInputStream.available: 35249
**CipherInputStream.available: 27057
**BufferedInputStream.read: 32768
(where's the 18865)?
**CipherInputStream.available: 10673
**CipherInputStream.available: 2481
**CipherInputStream.available: 0
**BufferedInputStream.read: 18864
**CipherInputStream.available: 0
**BufferedInputStream.read: 1
EOF

Code

int BUFFER_SIZE = 32768;
BufferedInputStream bis = null;
CipherInputStream cis = null;
try {
    // returns an AES256 CTR cipher with no padding
    Cipher cipher = getCipher();

    cis = new CipherInputStream(fis, cipher) {

        @Override
        public int available() throws IOException {
            // The cipher input stream always returns 0 because
            // in certain implementations (padded ciphers), a
            // standard implementation may not be able to
            // accurately determine the number of available
            // bytes. In our case, however, we are not using a
            // padded cipher and the number of available bytes
            // 'should' be the same as the number available from
            // the underlying stream.

            int available = in.available();
            System.out.println("**CipherInputStream.available: "
                + available);
            return available;
        }
    };
    bis = new BufferedInputStream(cis, BUFFER_SIZE);

    byte[] buf = new byte[BUFFER_SIZE];
    int read = 0;
    while (read >= 0) {
        read = bis.read(buf, 0, BUFFER_SIZE);
        if (read >= 0) {
            System.out.println("**BufferedInputStream.read: " + read);
        }
    }
    System.out.println("EOF");
}
finally {
    if (cis != null) {
        try {
            cis.close();
        }
        catch (IOException exp) {
            // ignore
        }
    }
    if (bis != null) {
        try {
            bis.close();
        }
        catch (IOException exp) {
            // ignore
        }
    }
}

I have tried creating my own BufferedInputStream and alter the read(byte[], int, int) method to not check the available() of the wrapped stream. This way, it will continue to read until a) the buffer is full, or b) end of file is reached, but i'm not sure if this the best approach.

Does anyone have any better suggestions or ideas on how to solve this?

like image 635
Erick Lichtas Avatar asked Jan 27 '26 21:01

Erick Lichtas


1 Answers

Forget about available() and all that code. Just wrap the CipherInputStream in a DataInputStream, declare a 32k buffer, and call readFully(). Two lines of code.

like image 125
user207421 Avatar answered Jan 29 '26 10:01

user207421



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!