private int getMoreData() throws IOException {
        if (done) {
            return -1;
        }

        int readLimit = ibuffer.length;
        if (partBytesRemain < ibuffer.length) {
            readLimit = (int) partBytesRemain;
        }

        int readin;
        if (partBytesRemain == 0) {
            readin = -1;
        } else {
            readin = in.read(ibuffer, 0, readLimit);
        }

        if (readin == -1) {
            ensureCapacity(0);
            try {
                ofinish = cipher.doFinal(obuffer, 0);
            } catch (Exception e) {
                throw new IOException(e);
            }

            int nextPart = part - 1;
            if (parts.containsKey(nextPart)) {
                // reset cipher
                PartPadding partPadding = parts.get(nextPart);
                try {
                    cipher.init(Cipher.DECRYPT_MODE, key, partPadding.getIv());
                } catch (Exception e) {
                    throw new IOException(e);
                }

                // update to the next part
                part = nextPart;

                // update the remaining bytes of the next part
                partBytesRemain = parts.get(nextPart).getSize();

                // Cannot call ByteStreams.skipFully since in may be shorter
                in.readNBytes(Constants.PADDING_BLOCK_SIZE);

                return ofinish;
            } else {
                done = true;
                if (ofinish == 0) {
                    return -1;
                } else {
                    return ofinish;
                }
            }
        }
        ensureCapacity(readin);
        try {
            ofinish = cipher.update(ibuffer, 0, readin, obuffer, ostart);
        } catch (ShortBufferException e) {
            throw new IOException(e);
        }

        partBytesRemain = partBytesRemain - readin;
        return ofinish;
    }
Private convenience function, read in data from the underlying
input stream and process them with cipher. This method is called
when the processed bytes inside obuffer has been exhausted.
<p>
Entry condition: ostart = ofinish
<p>
Exit condition: ostart = 0 AND ostart <= ofinish
<p>
return (ofinish-ostart) (we have this many bytes for you)
return 0 (no data now, but could have more later)
return -1 (absolutely no more data)
<p>
Note: Exceptions are only thrown after the stream is completely read.
For AEAD ciphers a read() of any length will internally cause the
whole stream to be read fully and verify the authentication tag before
returning decrypted data or exceptions.
src/main/java/org/gaul/s3proxy/crypto/DecryptionInputStream.java