• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package javax.crypto;
19 
20 import java.io.FilterInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.security.GeneralSecurityException;
24 
25 /**
26  * This class wraps an {@code InputStream} and a cipher so that {@code read()}
27  * methods return data that are read from the underlying {@code InputStream} and
28  * processed by the cipher.
29  * <p>
30  * The cipher must be initialized for the requested operation before being used
31  * by a {@code CipherInputStream}. For example, if a cipher initialized for
32  * decryption is used with a {@code CipherInputStream}, the {@code
33  * CipherInputStream} tries to read the data an decrypt them before returning.
34  */
35 public class CipherInputStream extends FilterInputStream {
36 
37     private final Cipher cipher;
38     private final int I_BUFFER_SIZE = 20;
39     private final byte[] i_buffer = new byte[I_BUFFER_SIZE];
40     private int index; // index of the bytes to return from o_buffer
41     private byte[] o_buffer;
42     private boolean finished;
43 
44     /**
45      * Creates a new {@code CipherInputStream} instance for an {@code
46      * InputStream} and a cipher.
47      *
48      * <p><strong>Warning:</strong> passing a null source creates an invalid
49      * {@code CipherInputStream}. All read operations on such a stream will
50      * fail.
51      *
52      * @param is
53      *            the input stream to read data from.
54      * @param c
55      *            the cipher to process the data with.
56      */
CipherInputStream(InputStream is, Cipher c)57     public CipherInputStream(InputStream is, Cipher c) {
58         super(is);
59         this.cipher = c;
60     }
61 
62     /**
63      * Creates a new {@code CipherInputStream} instance for an {@code
64      * InputStream} without a cipher.
65      * <p>
66      * A {@code NullCipher} is created and used to process the data.
67      *
68      * @param is
69      *            the input stream to read data from.
70      */
CipherInputStream(InputStream is)71     protected CipherInputStream(InputStream is) {
72         this(is, new NullCipher());
73     }
74 
75     /**
76      * Reads the next byte from this cipher input stream.
77      *
78      * @return the next byte, or {@code -1} if the end of the stream is reached.
79      * @throws IOException
80      *             if an error occurs.
81      */
82     @Override
read()83     public int read() throws IOException {
84         if (finished) {
85             return ((o_buffer == null) || (index == o_buffer.length))
86                             ? -1
87                             : o_buffer[index++] & 0xFF;
88         }
89         if ((o_buffer != null) && (index < o_buffer.length)) {
90             return o_buffer[index++] & 0xFF;
91         }
92         index = 0;
93         o_buffer = null;
94         int num_read;
95         while (o_buffer == null) {
96             if ((num_read = in.read(i_buffer)) == -1) {
97                 try {
98                     o_buffer = cipher.doFinal();
99                 } catch (Exception e) {
100                     throw new IOException(e.getMessage());
101                 }
102                 finished = true;
103                 break;
104             }
105             o_buffer = cipher.update(i_buffer, 0, num_read);
106         }
107         return read();
108     }
109 
110     /**
111      * Reads the next {@code b.length} bytes from this input stream into buffer
112      * {@code b}.
113      *
114      * @param b
115      *            the buffer to be filled with data.
116      * @return the number of bytes filled into buffer {@code b}, or {@code -1}
117      *         if the end of the stream is reached.
118      * @throws IOException
119      *             if an error occurs.
120      */
121     @Override
read(byte[] b)122     public int read(byte[] b) throws IOException {
123         return read(b, 0, b.length);
124     }
125 
126     /**
127      * Reads the next {@code len} bytes from this input stream into buffer
128      * {@code b} starting at offset {@code off}.
129      * <p>
130      * if {@code b} is {@code null}, the next {@code len} bytes are read and
131      * discarded.
132      *
133      * @param b
134      *            the buffer to be filled with data.
135      * @param off
136      *            the offset to start in the buffer.
137      * @param len
138      *            the maximum number of bytes to read.
139      * @return the number of bytes filled into buffer {@code b}, or {@code -1}
140      *         of the of the stream is reached.
141      * @throws IOException
142      *             if an error occurs.
143      * @throws NullPointerException
144      *             if the underlying input stream is {@code null}.
145      */
146     @Override
read(byte[] b, int off, int len)147     public int read(byte[] b, int off, int len) throws IOException {
148         if (in == null) {
149             throw new NullPointerException("Underlying input stream is null");
150         }
151 
152         int read_b;
153         int i;
154         for (i=0; i<len; i++) {
155             if ((read_b = read()) == -1) {
156                 return (i == 0) ? -1 : i;
157             }
158             if (b != null) {
159                 b[off+i] = (byte) read_b;
160             }
161         }
162         return i;
163     }
164 
165     /**
166      * Skips up to n bytes from this input stream.
167      * <p>
168      * The number of bytes skipped depends on the result of a call to
169      * {@link CipherInputStream#available() available}. The smaller of n and the
170      * result are the number of bytes being skipped.
171      *
172      * @param n
173      *            the number of bytes that should be skipped.
174      * @return the number of bytes actually skipped.
175      * @throws IOException
176      *             if an error occurs
177      */
178     @Override
skip(long n)179     public long skip(long n) throws IOException {
180         long i = 0;
181         int available = available();
182         if (available < n) {
183             n = available;
184         }
185         while ((i < n) && (read() != -1)) {
186             i++;
187         }
188         return i;
189     }
190 
191     @Override
available()192     public int available() throws IOException {
193         return 0;
194     }
195 
196     /**
197      * Closes this {@code CipherInputStream}, also closes the underlying input
198      * stream and call {@code doFinal} on the cipher object.
199      *
200      * @throws IOException
201      *             if an error occurs.
202      */
203     @Override
close()204     public void close() throws IOException {
205         in.close();
206         try {
207             cipher.doFinal();
208         } catch (GeneralSecurityException ignore) {
209             //do like RI does
210         }
211 
212     }
213 
214     /**
215      * Returns whether this input stream supports {@code mark} and
216      * {@code reset}, which it does not.
217      *
218      * @return false, since this input stream does not support {@code mark} and
219      *         {@code reset}.
220      */
221     @Override
markSupported()222     public boolean markSupported() {
223         return false;
224     }
225 }
226