Path: blob/master/src/java.base/share/classes/javax/crypto/CipherInputStream.java
41152 views
/*1* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.crypto;2627import java.io.InputStream;28import java.io.FilterInputStream;29import java.io.IOException;30import javax.crypto.BadPaddingException;31import javax.crypto.IllegalBlockSizeException;3233/**34* A CipherInputStream is composed of an InputStream and a Cipher so35* that read() methods return data that are read in from the36* underlying InputStream but have been additionally processed by the37* Cipher. The Cipher must be fully initialized before being used by38* a CipherInputStream.39*40* <p> For example, if the Cipher is initialized for decryption, the41* CipherInputStream will attempt to read in data and decrypt them,42* before returning the decrypted data.43*44* <p> This class adheres strictly to the semantics, especially the45* failure semantics, of its ancestor classes46* java.io.FilterInputStream and java.io.InputStream. This class has47* exactly those methods specified in its ancestor classes, and48* overrides them all. Moreover, this class catches all exceptions49* that are not thrown by its ancestor classes. In particular, the50* <code>skip</code> method skips, and the <code>available</code>51* method counts only data that have been processed by the encapsulated Cipher.52* This class may catch BadPaddingException and other exceptions thrown by53* failed integrity checks during decryption. These exceptions are not54* re-thrown, so the client may not be informed that integrity checks55* failed. Because of this behavior, this class may not be suitable56* for use with decryption in an authenticated mode of operation (e.g. GCM).57* Applications that require authenticated encryption can use the Cipher API58* directly as an alternative to using this class.59*60* <p> It is crucial for a programmer using this class not to use61* methods that are not defined or overridden in this class (such as a62* new method or constructor that is later added to one of the super63* classes), because the design and implementation of those methods64* are unlikely to have considered security impact with regard to65* CipherInputStream.66*67* @author Li Gong68* @see java.io.InputStream69* @see java.io.FilterInputStream70* @see javax.crypto.Cipher71* @see javax.crypto.CipherOutputStream72*73* @since 1.474*/7576public class CipherInputStream extends FilterInputStream {7778// the cipher engine to use to process stream data79private Cipher cipher;8081// the underlying input stream82private InputStream input;8384/* the buffer holding data that have been read in from the85underlying stream, but have not been processed by the cipher86engine. the size 512 bytes is somewhat randomly chosen */87private byte[] ibuffer = new byte[512];8889// having reached the end of the underlying input stream90private boolean done = false;9192/* the buffer holding data that have been processed by the cipher93engine, but have not been read out */94private byte[] obuffer = null;95// the offset pointing to the next "new" byte96private int ostart = 0;97// the offset pointing to the last "new" byte98private int ofinish = 0;99// stream status100private boolean closed = false;101102/**103* Ensure obuffer is big enough for the next update or doFinal104* operation, given the input length <code>inLen</code> (in bytes)105* The ostart and ofinish indices are reset to 0.106*107* @param inLen the input length (in bytes)108*/109private void ensureCapacity(int inLen) {110int minLen = cipher.getOutputSize(inLen);111if (obuffer == null || obuffer.length < minLen) {112obuffer = new byte[minLen];113}114ostart = 0;115ofinish = 0;116}117118/**119* Private convenience function, read in data from the underlying120* input stream and process them with cipher. This method is called121* when the processed bytes inside obuffer has been exhausted.122*123* Entry condition: ostart = ofinish124*125* Exit condition: ostart = 0 AND ostart <= ofinish126*127* return (ofinish-ostart) (we have this many bytes for you)128* return 0 (no data now, but could have more later)129* return -1 (absolutely no more data)130*131* Note: Exceptions are only thrown after the stream is completely read.132* For AEAD ciphers a read() of any length will internally cause the133* whole stream to be read fully and verify the authentication tag before134* returning decrypted data or exceptions.135*/136private int getMoreData() throws IOException {137if (done) return -1;138int readin = input.read(ibuffer);139140if (readin == -1) {141done = true;142ensureCapacity(0);143try {144ofinish = cipher.doFinal(obuffer, 0);145} catch (IllegalBlockSizeException | BadPaddingException146| ShortBufferException e) {147throw new IOException(e);148}149if (ofinish == 0) {150return -1;151} else {152return ofinish;153}154}155ensureCapacity(readin);156try {157ofinish = cipher.update(ibuffer, 0, readin, obuffer, ostart);158} catch (IllegalStateException e) {159throw e;160} catch (ShortBufferException e) {161throw new IOException(e);162}163return ofinish;164}165166/**167* Constructs a CipherInputStream from an InputStream and a168* Cipher.169* <br>Note: if the specified input stream or cipher is170* null, a NullPointerException may be thrown later when171* they are used.172* @param is the to-be-processed input stream173* @param c an initialized Cipher object174*/175public CipherInputStream(InputStream is, Cipher c) {176super(is);177input = is;178cipher = c;179}180181/**182* Constructs a CipherInputStream from an InputStream without183* specifying a Cipher. This has the effect of constructing a184* CipherInputStream using a NullCipher.185* <br>Note: if the specified input stream is null, a186* NullPointerException may be thrown later when it is used.187* @param is the to-be-processed input stream188*/189protected CipherInputStream(InputStream is) {190super(is);191input = is;192cipher = new NullCipher();193}194195/**196* Reads the next byte of data from this input stream. The value197* byte is returned as an <code>int</code> in the range198* <code>0</code> to <code>255</code>. If no byte is available199* because the end of the stream has been reached, the value200* <code>-1</code> is returned. This method blocks until input data201* is available, the end of the stream is detected, or an exception202* is thrown.203*204* @return the next byte of data, or <code>-1</code> if the end of the205* stream is reached.206* @exception IOException if an I/O error occurs.207*/208@Override209public int read() throws IOException {210if (ostart >= ofinish) {211// we loop for new data as the spec says we are blocking212int i = 0;213while (i == 0) i = getMoreData();214if (i == -1) return -1;215}216return ((int) obuffer[ostart++] & 0xff);217};218219/**220* Reads up to <code>b.length</code> bytes of data from this input221* stream into an array of bytes.222* <p>223* The <code>read</code> method of <code>InputStream</code> calls224* the <code>read</code> method of three arguments with the arguments225* <code>b</code>, <code>0</code>, and <code>b.length</code>.226*227* @param b the buffer into which the data is read.228* @return the total number of bytes read into the buffer, or229* <code>-1</code> is there is no more data because the end of230* the stream has been reached.231* @exception IOException if an I/O error occurs.232* @see java.io.InputStream#read(byte[], int, int)233*/234@Override235public int read(byte b[]) throws IOException {236return read(b, 0, b.length);237}238239/**240* Reads up to <code>len</code> bytes of data from this input stream241* into an array of bytes. This method blocks until some input is242* available. If the first argument is <code>null,</code> up to243* <code>len</code> bytes are read and discarded.244*245* @param b the buffer into which the data is read.246* @param off the start offset in the destination array247* <code>buf</code>248* @param len the maximum number of bytes read.249* @return the total number of bytes read into the buffer, or250* <code>-1</code> if there is no more data because the end of251* the stream has been reached.252* @exception IOException if an I/O error occurs.253* @see java.io.InputStream#read()254*/255@Override256public int read(byte b[], int off, int len) throws IOException {257if (ostart >= ofinish) {258// we loop for new data as the spec says we are blocking259int i = 0;260while (i == 0) i = getMoreData();261if (i == -1) return -1;262}263if (len <= 0) {264return 0;265}266int available = ofinish - ostart;267if (len < available) available = len;268if (b != null) {269System.arraycopy(obuffer, ostart, b, off, available);270}271ostart = ostart + available;272return available;273}274275/**276* Skips <code>n</code> bytes of input from the bytes that can be read277* from this input stream without blocking.278*279* <p>Fewer bytes than requested might be skipped.280* The actual number of bytes skipped is equal to <code>n</code> or281* the result of a call to282* {@link #available() available},283* whichever is smaller.284* If <code>n</code> is less than zero, no bytes are skipped.285*286* <p>The actual number of bytes skipped is returned.287*288* @param n the number of bytes to be skipped.289* @return the actual number of bytes skipped.290* @exception IOException if an I/O error occurs.291*/292@Override293public long skip(long n) throws IOException {294int available = ofinish - ostart;295if (n > available) {296n = available;297}298if (n < 0) {299return 0;300}301ostart += n;302return n;303}304305/**306* Returns the number of bytes that can be read from this input307* stream without blocking. The <code>available</code> method of308* <code>InputStream</code> returns <code>0</code>. This method309* <B>should</B> be overridden by subclasses.310*311* @return the number of bytes that can be read from this input stream312* without blocking.313* @exception IOException if an I/O error occurs.314*/315@Override316public int available() throws IOException {317return (ofinish - ostart);318}319320/**321* Closes this input stream and releases any system resources322* associated with the stream.323* <p>324* The <code>close</code> method of <code>CipherInputStream</code>325* calls the <code>close</code> method of its underlying input326* stream.327*328* @exception IOException if an I/O error occurs.329*/330@Override331public void close() throws IOException {332if (closed) {333return;334}335closed = true;336input.close();337338// Throw away the unprocessed data and throw no crypto exceptions.339// AEAD ciphers are fully readed before closing. Any authentication340// exceptions would occur while reading.341if (!done) {342ensureCapacity(0);343try {344cipher.doFinal(obuffer, 0);345} catch (BadPaddingException | IllegalBlockSizeException346| ShortBufferException ex) {347// Catch exceptions as the rest of the stream is unused.348}349}350obuffer = null;351}352353/**354* Tests if this input stream supports the <code>mark</code>355* and <code>reset</code> methods, which it does not.356*357* @return <code>false</code>, since this class does not support the358* <code>mark</code> and <code>reset</code> methods.359* @see java.io.InputStream#mark(int)360* @see java.io.InputStream#reset()361*/362@Override363public boolean markSupported() {364return false;365}366}367368369