src/java.base/share/classes/javax/crypto/CipherInputStream.java
changeset 47216 71c04702a3d5
parent 44999 b1c62cd70983
child 51759 ac6e9a2ebc04
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package javax.crypto;
       
    27 
       
    28 import java.io.InputStream;
       
    29 import java.io.FilterInputStream;
       
    30 import java.io.IOException;
       
    31 import javax.crypto.BadPaddingException;
       
    32 import javax.crypto.IllegalBlockSizeException;
       
    33 
       
    34 /**
       
    35  * A CipherInputStream is composed of an InputStream and a Cipher so
       
    36  * that read() methods return data that are read in from the
       
    37  * underlying InputStream but have been additionally processed by the
       
    38  * Cipher.  The Cipher must be fully initialized before being used by
       
    39  * a CipherInputStream.
       
    40  *
       
    41  * <p> For example, if the Cipher is initialized for decryption, the
       
    42  * CipherInputStream will attempt to read in data and decrypt them,
       
    43  * before returning the decrypted data.
       
    44  *
       
    45  * <p> This class adheres strictly to the semantics, especially the
       
    46  * failure semantics, of its ancestor classes
       
    47  * java.io.FilterInputStream and java.io.InputStream.  This class has
       
    48  * exactly those methods specified in its ancestor classes, and
       
    49  * overrides them all.  Moreover, this class catches all exceptions
       
    50  * that are not thrown by its ancestor classes.  In particular, the
       
    51  * <code>skip</code> method skips, and the <code>available</code>
       
    52  * method counts only data that have been processed by the encapsulated Cipher.
       
    53  *
       
    54  * <p> It is crucial for a programmer using this class not to use
       
    55  * methods that are not defined or overriden in this class (such as a
       
    56  * new method or constructor that is later added to one of the super
       
    57  * classes), because the design and implementation of those methods
       
    58  * are unlikely to have considered security impact with regard to
       
    59  * CipherInputStream.
       
    60  *
       
    61  * @author  Li Gong
       
    62  * @see     java.io.InputStream
       
    63  * @see     java.io.FilterInputStream
       
    64  * @see     javax.crypto.Cipher
       
    65  * @see     javax.crypto.CipherOutputStream
       
    66  *
       
    67  * @since 1.4
       
    68  */
       
    69 
       
    70 public class CipherInputStream extends FilterInputStream {
       
    71 
       
    72     // the cipher engine to use to process stream data
       
    73     private Cipher cipher;
       
    74 
       
    75     // the underlying input stream
       
    76     private InputStream input;
       
    77 
       
    78     /* the buffer holding data that have been read in from the
       
    79        underlying stream, but have not been processed by the cipher
       
    80        engine. the size 512 bytes is somewhat randomly chosen */
       
    81     private byte[] ibuffer = new byte[512];
       
    82 
       
    83     // having reached the end of the underlying input stream
       
    84     private boolean done = false;
       
    85 
       
    86     /* the buffer holding data that have been processed by the cipher
       
    87        engine, but have not been read out */
       
    88     private byte[] obuffer;
       
    89     // the offset pointing to the next "new" byte
       
    90     private int ostart = 0;
       
    91     // the offset pointing to the last "new" byte
       
    92     private int ofinish = 0;
       
    93     // stream status
       
    94     private boolean closed = false;
       
    95 
       
    96     /*
       
    97      * private convenience function.
       
    98      *
       
    99      * Entry condition: ostart = ofinish
       
   100      *
       
   101      * Exit condition: ostart <= ofinish
       
   102      *
       
   103      * return (ofinish-ostart) (we have this many bytes for you)
       
   104      * return 0 (no data now, but could have more later)
       
   105      * return -1 (absolutely no more data)
       
   106      *
       
   107      * Note:  Exceptions are only thrown after the stream is completely read.
       
   108      * For AEAD ciphers a read() of any length will internally cause the
       
   109      * whole stream to be read fully and verify the authentication tag before
       
   110      * returning decrypted data or exceptions.
       
   111      */
       
   112     private int getMoreData() throws IOException {
       
   113         if (done) return -1;
       
   114         int readin = input.read(ibuffer);
       
   115         if (readin == -1) {
       
   116             done = true;
       
   117             try {
       
   118                 obuffer = cipher.doFinal();
       
   119             } catch (IllegalBlockSizeException | BadPaddingException e) {
       
   120                 obuffer = null;
       
   121                 throw new IOException(e);
       
   122             }
       
   123             if (obuffer == null)
       
   124                 return -1;
       
   125             else {
       
   126                 ostart = 0;
       
   127                 ofinish = obuffer.length;
       
   128                 return ofinish;
       
   129             }
       
   130         }
       
   131         try {
       
   132             obuffer = cipher.update(ibuffer, 0, readin);
       
   133         } catch (IllegalStateException e) {
       
   134             obuffer = null;
       
   135             throw e;
       
   136         }
       
   137         ostart = 0;
       
   138         if (obuffer == null)
       
   139             ofinish = 0;
       
   140         else ofinish = obuffer.length;
       
   141         return ofinish;
       
   142     }
       
   143 
       
   144     /**
       
   145      * Constructs a CipherInputStream from an InputStream and a
       
   146      * Cipher.
       
   147      * <br>Note: if the specified input stream or cipher is
       
   148      * null, a NullPointerException may be thrown later when
       
   149      * they are used.
       
   150      * @param is the to-be-processed input stream
       
   151      * @param c an initialized Cipher object
       
   152      */
       
   153     public CipherInputStream(InputStream is, Cipher c) {
       
   154         super(is);
       
   155         input = is;
       
   156         cipher = c;
       
   157     }
       
   158 
       
   159     /**
       
   160      * Constructs a CipherInputStream from an InputStream without
       
   161      * specifying a Cipher. This has the effect of constructing a
       
   162      * CipherInputStream using a NullCipher.
       
   163      * <br>Note: if the specified input stream is null, a
       
   164      * NullPointerException may be thrown later when it is used.
       
   165      * @param is the to-be-processed input stream
       
   166      */
       
   167     protected CipherInputStream(InputStream is) {
       
   168         super(is);
       
   169         input = is;
       
   170         cipher = new NullCipher();
       
   171     }
       
   172 
       
   173     /**
       
   174      * Reads the next byte of data from this input stream. The value
       
   175      * byte is returned as an <code>int</code> in the range
       
   176      * <code>0</code> to <code>255</code>. If no byte is available
       
   177      * because the end of the stream has been reached, the value
       
   178      * <code>-1</code> is returned. This method blocks until input data
       
   179      * is available, the end of the stream is detected, or an exception
       
   180      * is thrown.
       
   181      *
       
   182      * @return  the next byte of data, or <code>-1</code> if the end of the
       
   183      *          stream is reached.
       
   184      * @exception  IOException  if an I/O error occurs.
       
   185      */
       
   186     public int read() throws IOException {
       
   187         if (ostart >= ofinish) {
       
   188             // we loop for new data as the spec says we are blocking
       
   189             int i = 0;
       
   190             while (i == 0) i = getMoreData();
       
   191             if (i == -1) return -1;
       
   192         }
       
   193         return ((int) obuffer[ostart++] & 0xff);
       
   194     };
       
   195 
       
   196     /**
       
   197      * Reads up to <code>b.length</code> bytes of data from this input
       
   198      * stream into an array of bytes.
       
   199      * <p>
       
   200      * The <code>read</code> method of <code>InputStream</code> calls
       
   201      * the <code>read</code> method of three arguments with the arguments
       
   202      * <code>b</code>, <code>0</code>, and <code>b.length</code>.
       
   203      *
       
   204      * @param      b   the buffer into which the data is read.
       
   205      * @return     the total number of bytes read into the buffer, or
       
   206      *             <code>-1</code> is there is no more data because the end of
       
   207      *             the stream has been reached.
       
   208      * @exception  IOException  if an I/O error occurs.
       
   209      * @see        java.io.InputStream#read(byte[], int, int)
       
   210      */
       
   211     public int read(byte b[]) throws IOException {
       
   212         return read(b, 0, b.length);
       
   213     }
       
   214 
       
   215     /**
       
   216      * Reads up to <code>len</code> bytes of data from this input stream
       
   217      * into an array of bytes. This method blocks until some input is
       
   218      * available. If the first argument is <code>null,</code> up to
       
   219      * <code>len</code> bytes are read and discarded.
       
   220      *
       
   221      * @param      b     the buffer into which the data is read.
       
   222      * @param      off   the start offset in the destination array
       
   223      *                   <code>buf</code>
       
   224      * @param      len   the maximum number of bytes read.
       
   225      * @return     the total number of bytes read into the buffer, or
       
   226      *             <code>-1</code> if there is no more data because the end of
       
   227      *             the stream has been reached.
       
   228      * @exception  IOException  if an I/O error occurs.
       
   229      * @see        java.io.InputStream#read()
       
   230      */
       
   231     public int read(byte b[], int off, int len) throws IOException {
       
   232         if (ostart >= ofinish) {
       
   233             // we loop for new data as the spec says we are blocking
       
   234             int i = 0;
       
   235             while (i == 0) i = getMoreData();
       
   236             if (i == -1) return -1;
       
   237         }
       
   238         if (len <= 0) {
       
   239             return 0;
       
   240         }
       
   241         int available = ofinish - ostart;
       
   242         if (len < available) available = len;
       
   243         if (b != null) {
       
   244             System.arraycopy(obuffer, ostart, b, off, available);
       
   245         }
       
   246         ostart = ostart + available;
       
   247         return available;
       
   248     }
       
   249 
       
   250     /**
       
   251      * Skips <code>n</code> bytes of input from the bytes that can be read
       
   252      * from this input stream without blocking.
       
   253      *
       
   254      * <p>Fewer bytes than requested might be skipped.
       
   255      * The actual number of bytes skipped is equal to <code>n</code> or
       
   256      * the result of a call to
       
   257      * {@link #available() available},
       
   258      * whichever is smaller.
       
   259      * If <code>n</code> is less than zero, no bytes are skipped.
       
   260      *
       
   261      * <p>The actual number of bytes skipped is returned.
       
   262      *
       
   263      * @param      n the number of bytes to be skipped.
       
   264      * @return     the actual number of bytes skipped.
       
   265      * @exception  IOException  if an I/O error occurs.
       
   266      */
       
   267     public long skip(long n) throws IOException {
       
   268         int available = ofinish - ostart;
       
   269         if (n > available) {
       
   270             n = available;
       
   271         }
       
   272         if (n < 0) {
       
   273             return 0;
       
   274         }
       
   275         ostart += n;
       
   276         return n;
       
   277     }
       
   278 
       
   279     /**
       
   280      * Returns the number of bytes that can be read from this input
       
   281      * stream without blocking. The <code>available</code> method of
       
   282      * <code>InputStream</code> returns <code>0</code>. This method
       
   283      * <B>should</B> be overridden by subclasses.
       
   284      *
       
   285      * @return     the number of bytes that can be read from this input stream
       
   286      *             without blocking.
       
   287      * @exception  IOException  if an I/O error occurs.
       
   288      */
       
   289     public int available() throws IOException {
       
   290         return (ofinish - ostart);
       
   291     }
       
   292 
       
   293     /**
       
   294      * Closes this input stream and releases any system resources
       
   295      * associated with the stream.
       
   296      * <p>
       
   297      * The <code>close</code> method of <code>CipherInputStream</code>
       
   298      * calls the <code>close</code> method of its underlying input
       
   299      * stream.
       
   300      *
       
   301      * @exception  IOException  if an I/O error occurs.
       
   302      */
       
   303     public void close() throws IOException {
       
   304         if (closed) {
       
   305             return;
       
   306         }
       
   307 
       
   308         closed = true;
       
   309         input.close();
       
   310 
       
   311         // Throw away the unprocessed data and throw no crypto exceptions.
       
   312         // AEAD ciphers are fully readed before closing.  Any authentication
       
   313         // exceptions would occur while reading.
       
   314         if (!done) {
       
   315             try {
       
   316                 cipher.doFinal();
       
   317             }
       
   318             catch (BadPaddingException | IllegalBlockSizeException ex) {
       
   319                 // Catch exceptions as the rest of the stream is unused.
       
   320             }
       
   321         }
       
   322         ostart = 0;
       
   323         ofinish = 0;
       
   324     }
       
   325 
       
   326     /**
       
   327      * Tests if this input stream supports the <code>mark</code>
       
   328      * and <code>reset</code> methods, which it does not.
       
   329      *
       
   330      * @return  <code>false</code>, since this class does not support the
       
   331      *          <code>mark</code> and <code>reset</code> methods.
       
   332      * @see     java.io.InputStream#mark(int)
       
   333      * @see     java.io.InputStream#reset()
       
   334      */
       
   335     public boolean markSupported() {
       
   336         return false;
       
   337     }
       
   338 }