mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
391 lines
14 KiB
Java
391 lines
14 KiB
Java
/*
|
|
* Copyright (c) 1997, 2024, 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.FilterInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
|
|
/**
|
|
* A {@code CipherInputStream} is composed of an {@code InputStream}
|
|
* and a {@code Cipher} object so that read() methods return data that are
|
|
* read in from the underlying {@code InputStream} but have been
|
|
* additionally processed by the {@code Cipher} object. The {@code Cipher}
|
|
* object must be fully initialized before being used by a
|
|
* {@code CipherInputStream}.
|
|
*
|
|
* <p> For example, if the {@code Cipher} object is initialized for decryption,
|
|
* the {@code CipherInputStream} will attempt to read in data and decrypt
|
|
* them, before returning the decrypted data.
|
|
*
|
|
* <p> This class adheres strictly to the semantics, especially the
|
|
* failure semantics, of its ancestor classes
|
|
* {@code java.io.FilterInputStream} and {@code 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
|
|
* {@code skip} method skips, and the {@code available}
|
|
* method counts only data that have been processed by the encapsulated
|
|
* {@code Cipher} object.
|
|
* This class may catch {@code BadPaddingException} and other exceptions
|
|
* thrown by failed integrity checks during decryption. These exceptions are not
|
|
* re-thrown, so the client may not be informed that integrity checks
|
|
* failed. Because of this behavior, this class may not be suitable
|
|
* for use with decryption in an authenticated mode of operation (e.g. GCM).
|
|
* Applications that require authenticated encryption can use the
|
|
* {@code Cipher} API directly as an alternative to using this class.
|
|
*
|
|
* <p> It is crucial for a programmer using this class not to use
|
|
* methods that are not defined or overridden 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
|
|
* {@code 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 final Cipher cipher;
|
|
|
|
// the underlying input stream
|
|
private final InputStream input;
|
|
|
|
/* the buffer holding data that have been read in from the
|
|
underlying stream, but have not been processed by the cipher
|
|
engine. */
|
|
private final byte[] ibuffer = new byte[8192];
|
|
|
|
// 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 = null;
|
|
// the offset pointing to the next "new" byte
|
|
private int ostart = 0;
|
|
// the offset pointing to the last "new" byte
|
|
private int ofinish = 0;
|
|
// stream status
|
|
private boolean closed = false;
|
|
|
|
/**
|
|
* Ensure obuffer is big enough for the next update or doFinal
|
|
* operation, given the input length {@code inLen} (in bytes)
|
|
* The ostart and ofinish indices are reset to 0.
|
|
*
|
|
* If obuffer is null/zero-sized, do not allocate a new buffer.
|
|
* This reduces allocation for authenticated decryption
|
|
* that never returns data from update
|
|
*
|
|
* @param inLen the input length (in bytes)
|
|
*/
|
|
private void ensureCapacity(int inLen) {
|
|
if (obuffer == null || obuffer.length == 0) {
|
|
return;
|
|
}
|
|
int minLen = cipher.getOutputSize(inLen);
|
|
if (obuffer.length < minLen) {
|
|
obuffer = new byte[minLen];
|
|
}
|
|
ostart = 0;
|
|
ofinish = 0;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* Entry condition: ostart = ofinish
|
|
*
|
|
* Exit condition: ostart = 0 AND 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)
|
|
*
|
|
* 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.
|
|
*/
|
|
private int getMoreData() throws IOException {
|
|
if (done) return -1;
|
|
int readin = input.read(ibuffer);
|
|
|
|
if (readin == -1) {
|
|
done = true;
|
|
ensureCapacity(0);
|
|
try {
|
|
if (obuffer != null && obuffer.length > 0) {
|
|
ofinish = cipher.doFinal(obuffer, 0);
|
|
} else {
|
|
obuffer = cipher.doFinal();
|
|
ofinish = (obuffer != null) ? obuffer.length : 0;
|
|
}
|
|
} catch (IllegalBlockSizeException | BadPaddingException
|
|
| ShortBufferException e) {
|
|
throw new IOException(e);
|
|
}
|
|
if (ofinish == 0) {
|
|
return -1;
|
|
} else {
|
|
return ofinish;
|
|
}
|
|
}
|
|
ensureCapacity(readin);
|
|
try {
|
|
// initial obuffer is assigned by update/doFinal;
|
|
// for AEAD decryption, obuffer is always null or zero-length here
|
|
if (obuffer != null && obuffer.length > 0) {
|
|
ofinish = cipher.update(ibuffer, 0, readin, obuffer, ostart);
|
|
} else {
|
|
obuffer = cipher.update(ibuffer, 0, readin);
|
|
ofinish = (obuffer != null) ? obuffer.length : 0;
|
|
}
|
|
} catch (IllegalStateException e) {
|
|
throw e;
|
|
} catch (ShortBufferException e) {
|
|
throw new IOException(e);
|
|
}
|
|
return ofinish;
|
|
}
|
|
|
|
/**
|
|
* Constructs a {@code CipherInputStream} from an
|
|
* {@code InputStream} and a {@code Cipher} object.
|
|
* <br>Note: if the specified input stream or cipher is
|
|
* {@code null}, a {@code NullPointerException} may be thrown later when
|
|
* they are used.
|
|
* @param is the to-be-processed input stream
|
|
* @param c an initialized {@code Cipher} object
|
|
*/
|
|
public CipherInputStream(InputStream is, Cipher c) {
|
|
super(is);
|
|
input = is;
|
|
cipher = c;
|
|
}
|
|
|
|
/**
|
|
* Constructs a {@code CipherInputStream} from an
|
|
* {@code InputStream} without specifying a {@code Cipher} object.
|
|
* This has the effect of constructing a {@code CipherInputStream}
|
|
* using a {@code NullCipher}.
|
|
* <br>Note: if the specified input stream is {@code null}, a
|
|
* {@code 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 {@code int} in the range
|
|
* {@code 0} to {@code 255}. If no byte is available
|
|
* because the end of the stream has been reached, the value
|
|
* {@code -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 {@code -1} if the end of the
|
|
* stream is reached.
|
|
* @exception IOException if an I/O error occurs.
|
|
*/
|
|
@Override
|
|
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 {@code b.length} bytes of data from this input
|
|
* stream into an array of bytes.
|
|
* <p>
|
|
* The {@code read} method of {@code InputStream} calls
|
|
* the {@code read} method of three arguments with the arguments
|
|
* {@code b}, {@code 0}, and {@code b.length}.
|
|
*
|
|
* @param b the buffer into which the data is read.
|
|
* @return the total number of bytes read into the buffer, or
|
|
* {@code -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)
|
|
*/
|
|
@Override
|
|
public int read(byte[] b) throws IOException {
|
|
return read(b, 0, b.length);
|
|
}
|
|
|
|
/**
|
|
* Reads up to {@code 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 {@code null}, up to
|
|
* {@code 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
|
|
* {@code buf}
|
|
* @param len the maximum number of bytes read.
|
|
* @return the total number of bytes read into the buffer, or
|
|
* {@code -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()
|
|
*/
|
|
@Override
|
|
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 {@code n} bytes of input from the bytes that can be read
|
|
* from this input stream without blocking.
|
|
*
|
|
* <p>Fewer bytes than requested might be skipped.
|
|
* The actual number of bytes skipped is equal to {@code n} or
|
|
* the result of a call to
|
|
* {@link #available() available},
|
|
* whichever is smaller.
|
|
* If {@code n} is less than zero, no bytes are skipped.
|
|
*
|
|
* <p>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.
|
|
*/
|
|
@Override
|
|
public long skip(long n) throws IOException {
|
|
int available = ofinish - ostart;
|
|
if (n > available) {
|
|
n = available;
|
|
}
|
|
if (n < 0) {
|
|
return 0;
|
|
}
|
|
ostart += (int) n;
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of bytes that can be read from this input
|
|
* stream without blocking. The {@code available} method of
|
|
* {@code InputStream} returns {@code 0}. This method
|
|
* <B>should</B> 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.
|
|
*/
|
|
@Override
|
|
public int available() throws IOException {
|
|
return (ofinish - ostart);
|
|
}
|
|
|
|
/**
|
|
* Closes this input stream and releases any system resources
|
|
* associated with the stream.
|
|
* <p>
|
|
* The {@code close} method of {@code CipherInputStream}
|
|
* calls the {@code close} method of its underlying input
|
|
* stream.
|
|
*
|
|
* @exception IOException if an I/O error occurs.
|
|
*/
|
|
@Override
|
|
public void close() throws IOException {
|
|
if (closed) {
|
|
return;
|
|
}
|
|
closed = true;
|
|
input.close();
|
|
|
|
// Throw away the unprocessed data and throw no crypto exceptions.
|
|
// AEAD ciphers are fully read before closing. Any authentication
|
|
// exceptions would occur while reading.
|
|
if (!done) {
|
|
ensureCapacity(0);
|
|
try {
|
|
if (obuffer != null && obuffer.length > 0) {
|
|
cipher.doFinal(obuffer, 0);
|
|
} else {
|
|
cipher.doFinal();
|
|
}
|
|
} catch (BadPaddingException | IllegalBlockSizeException
|
|
| ShortBufferException ex) {
|
|
// Catch exceptions as the rest of the stream is unused.
|
|
}
|
|
}
|
|
obuffer = null;
|
|
}
|
|
|
|
/**
|
|
* Tests if this input stream supports the {@code mark}
|
|
* and {@code reset} methods, which it does not.
|
|
*
|
|
* @return {@code false}, since this class does not support the
|
|
* {@code mark} and {@code reset} methods.
|
|
* @see java.io.InputStream#mark(int)
|
|
* @see java.io.InputStream#reset()
|
|
*/
|
|
@Override
|
|
public boolean markSupported() {
|
|
return false;
|
|
}
|
|
}
|