package javax.crypto; import java.io.InputStream; import java.io.IOException; import java.io.FilterInputStream; /** * 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.
*
* @since JCE1.2
* @see InputStream
* @see FilterInputStream
* @see Cipher
* @see CipherOutputStream
*/
public class CipherInputStream
extends FilterInputStream
{
private Cipher c;
private byte[] buf;
private byte[] inBuf;
private int bufOff;
private int maxBuf;
private boolean finalized;
private static final int INPUT_BUF_SIZE = 2048;
/**
* Constructs a CipherInputStream from an InputStream and a
* Cipher.
*/
public CipherInputStream(
InputStream is,
Cipher c)
{
super(is);
this.c = c;
buf = new byte[c.getOutputSize(INPUT_BUF_SIZE)];
inBuf = new byte[INPUT_BUF_SIZE];
}
/**
* Constructs a CipherInputStream from an InputStream without
* specifying a Cipher. This has the effect of constructing a
* CipherInputStream using a NullCipher.
*/
protected CipherInputStream(
InputStream is)
{
this(is, new NullCipher());
}
/**
* grab the next chunk of input from the underlying input stream
*/
private int nextChunk()
throws IOException
{
int available = super.available();
// must always try to read 1 byte!
// some buggy InputStreams return < 0!
if (available <= 0)
{
available = 1;
}
if (available > inBuf.length)
{
available = super.read(inBuf, 0, inBuf.length);
}
else
{
available = super.read(inBuf, 0, available);
}
if (available < 0)
{
if (finalized)
{
return -1;
}
try
{
buf = c.doFinal();
}
catch (Exception e)
{
throw new IOException("error processing stream: " + e.toString());
}
bufOff = 0;
if (buf != null)
{
maxBuf = buf.length;
}
else
{
maxBuf = 0;
}
finalized = true;
if (bufOff == maxBuf)
{
return -1;
}
}
else
{
bufOff = 0;
try
{
maxBuf = c.update(inBuf, 0, available, buf, 0);
}
catch (Exception e)
{
throw new IOException("error processing stream: " + e.toString());
}
if (maxBuf == 0) // not enough bytes read for first block...
{
return nextChunk();
}
}
return maxBuf;
}
/**
* 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 (bufOff == maxBuf)
{
if (nextChunk() < 0)
{
return -1;
}
}
return buf[bufOff++] & 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.
* @since JCE1.2
* @see #read(byte[], int, int)
*/
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 of the data.
* @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.
* @since JCE1.2
* @see #read()
*/
public int read(
byte[] b,
int off,
int len)
throws IOException
{
if (bufOff == maxBuf)
{
if (nextChunk() < 0)
{
return -1;
}
}
int available = maxBuf - bufOff;
if (len > available)
{
System.arraycopy(buf, bufOff, b, off, available);
bufOff = maxBuf;
return available;
}
else
{
System.arraycopy(buf, bufOff, b, off, len);
bufOff += len;
return len;
}
}
/**
* 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 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
{
if (n <= 0)
{
return 0;
}
int available = maxBuf - bufOff;
if (n > available)
{
bufOff = maxBuf;
return available;
}
else
{
bufOff += (int)n;
return (int)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 maxBuf - bufOff;
}
/**
* 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
{
super.close();
}
/**
* 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.
* @since JCE1.2
* @see #mark(int)
* @see #reset()
*/
public boolean markSupported()
{
return false;
}
}