/* * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.crypto; import java.io.*; /** * A CipherInputStream is composed of an InputStream and a Cipher so * that read() methods return data that are read in from the * underlying InputStream but have been additionally processed by the * Cipher. The Cipher must be fully initialized before being used by * a CipherInputStream. * *
For example, if the Cipher is initialized for decryption, the * CipherInputStream will attempt to read in data and decrypt them, * before returning the decrypted data. * *
This class adheres strictly to the semantics, especially the
* failure semantics, of its ancestor classes
* java.io.FilterInputStream and java.io.InputStream. This class has
* exactly those methods specified in its ancestor classes, and
* overrides them all. Moreover, this class catches all exceptions
* that are not thrown by its ancestor classes. In particular, the
* skip
method skips, and the available
* method counts only data that have been processed by the encapsulated Cipher.
*
*
It is crucial for a programmer using this class not to use
* methods that are not defined or overriden in this class (such as a
* new method or constructor that is later added to one of the super
* classes), because the design and implementation of those methods
* are unlikely to have considered security impact with regard to
* CipherInputStream.
*
* @author Li Gong
* @see java.io.InputStream
* @see java.io.FilterInputStream
* @see javax.crypto.Cipher
* @see javax.crypto.CipherOutputStream
*
* @since 1.4
*/
public class CipherInputStream extends FilterInputStream {
// the cipher engine to use to process stream data
private Cipher cipher;
// the underlying input stream
private InputStream input;
/* the buffer holding data that have been read in from the
underlying stream, but have not been processed by the cipher
engine. the size 512 bytes is somewhat randomly chosen */
private byte[] ibuffer = new byte[512];
// having reached the end of the underlying input stream
private boolean done = false;
/* the buffer holding data that have been processed by the cipher
engine, but have not been read out */
private byte[] obuffer;
// the offset pointing to the next "new" byte
private int ostart = 0;
// the offset pointing to the last "new" byte
private int ofinish = 0;
/**
* private convenience function.
*
* Entry condition: ostart = ofinish
*
* Exit condition: ostart <= ofinish
*
* 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)
*/
private int getMoreData() throws IOException {
if (done) return -1;
int readin = input.read(ibuffer);
if (readin == -1) {
done = true;
try {
obuffer = cipher.doFinal();
}
catch (IllegalBlockSizeException e) {obuffer = null;}
catch (BadPaddingException e) {obuffer = null;}
if (obuffer == null)
return -1;
else {
ostart = 0;
ofinish = obuffer.length;
return ofinish;
}
}
try {
obuffer = cipher.update(ibuffer, 0, readin);
} catch (IllegalStateException e) {obuffer = null;};
ostart = 0;
if (obuffer == null)
ofinish = 0;
else ofinish = obuffer.length;
return ofinish;
}
/**
* Constructs a CipherInputStream from an InputStream and a
* Cipher.
*
Note: if the specified input stream or cipher is
* null, a NullPointerException may be thrown later when
* they are used.
* @param is the to-be-processed input stream
* @param c an initialized Cipher object
*/
public CipherInputStream(InputStream is, Cipher c) {
super(is);
input = is;
cipher = c;
}
/**
* Constructs a CipherInputStream from an InputStream without
* specifying a Cipher. This has the effect of constructing a
* CipherInputStream using a NullCipher.
*
Note: if the specified input stream is null, a
* NullPointerException may be thrown later when it is used.
* @param is the to-be-processed input stream
*/
protected CipherInputStream(InputStream is) {
super(is);
input = is;
cipher = new NullCipher();
}
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an int
in the range
* 0
to 255
. If no byte is available
* because the end of the stream has been reached, the value
* -1
is returned. This method blocks until input data
* is available, the end of the stream is detected, or an exception
* is thrown.
*
*
* @return the next byte of data, or -1
if the end of the
* stream is reached.
* @exception IOException if an I/O error occurs.
* @since JCE1.2
*/
public int read() throws IOException {
if (ostart >= ofinish) {
// we loop for new data as the spec says we are blocking
int i = 0;
while (i == 0) i = getMoreData();
if (i == -1) return -1;
}
return ((int) obuffer[ostart++] & 0xff);
};
/**
* Reads up to b.length
bytes of data from this input
* stream into an array of bytes.
*
* The read
method of InputStream
calls
* the read
method of three arguments with the arguments
* b
, 0
, and b.length
.
*
* @param b the buffer into which the data is read.
* @return the total number of bytes read into the buffer, or
* -1
is there is no more data because the end of
* the stream has been reached.
* @exception IOException if an I/O error occurs.
* @see java.io.InputStream#read(byte[], int, int)
* @since JCE1.2
*/
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
/**
* Reads up to len
bytes of data from this input stream
* into an array of bytes. This method blocks until some input is
* available. If the first argument is null,
up to
* len
bytes are read and discarded.
*
* @param b the buffer into which the data is read.
* @param off the start offset in the destination array
* buf
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or
* -1
if there is no more data because the end of
* the stream has been reached.
* @exception IOException if an I/O error occurs.
* @see java.io.InputStream#read()
* @since JCE1.2
*/
public int read(byte b[], int off, int len) throws IOException {
if (ostart >= ofinish) {
// we loop for new data as the spec says we are blocking
int i = 0;
while (i == 0) i = getMoreData();
if (i == -1) return -1;
}
if (len <= 0) {
return 0;
}
int available = ofinish - ostart;
if (len < available) available = len;
if (b != null) {
System.arraycopy(obuffer, ostart, b, off, available);
}
ostart = ostart + available;
return available;
}
/**
* Skips n
bytes of input from the bytes that can be read
* from this input stream without blocking.
*
*
Fewer bytes than requested might be skipped.
* The actual number of bytes skipped is equal to n
or
* the result of a call to
* {@link #available() available
},
* whichever is smaller.
* If n
is less than zero, no bytes are skipped.
*
*
The actual number of bytes skipped is returned.
*
* @param n the number of bytes to be skipped.
* @return the actual number of bytes skipped.
* @exception IOException if an I/O error occurs.
* @since JCE1.2
*/
public long skip(long n) throws IOException {
int available = ofinish - ostart;
if (n > available) {
n = available;
}
if (n < 0) {
return 0;
}
ostart += n;
return n;
}
/**
* Returns the number of bytes that can be read from this input
* stream without blocking. The available
method of
* InputStream
returns 0
. This method
* should be overridden by subclasses.
*
* @return the number of bytes that can be read from this input stream
* without blocking.
* @exception IOException if an I/O error occurs.
* @since JCE1.2
*/
public int available() throws IOException {
return (ofinish - ostart);
}
/**
* Closes this input stream and releases any system resources
* associated with the stream.
*
* The close
method of CipherInputStream
* calls the close
method of its underlying input
* stream.
*
* @exception IOException if an I/O error occurs.
* @since JCE1.2
*/
public void close() throws IOException {
input.close();
try {
// throw away the unprocessed data
cipher.doFinal();
}
catch (BadPaddingException ex) {
}
catch (IllegalBlockSizeException ex) {
}
ostart = 0;
ofinish = 0;
}
/**
* Tests if this input stream supports the mark
* and reset
methods, which it does not.
*
* @return false
, since this class does not support the
* mark
and reset
methods.
* @see java.io.InputStream#mark(int)
* @see java.io.InputStream#reset()
* @since JCE1.2
*/
public boolean markSupported() {
return false;
}
}