1/*
2 * Copyright (c) 1997, 2013, 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
26package javax.crypto;
27
28import java.io.*;
29
30/**
31 * A CipherOutputStream is composed of an OutputStream and a Cipher so
32 * that write() methods first process the data before writing them out
33 * to the underlying OutputStream.  The cipher must be fully
34 * initialized before being used by a CipherOutputStream.
35 *
36 * <p> For example, if the cipher is initialized for encryption, the
37 * CipherOutputStream will attempt to encrypt data before writing out the
38 * encrypted data.
39 *
40 * <p> This class adheres strictly to the semantics, especially the
41 * failure semantics, of its ancestor classes
42 * java.io.OutputStream and java.io.FilterOutputStream.  This class
43 * has exactly those methods specified in its ancestor classes, and
44 * overrides them all.  Moreover, this class catches all exceptions
45 * that are not thrown by its ancestor classes.
46 *
47 * <p> It is crucial for a programmer using this class not to use
48 * methods that are not defined or overriden in this class (such as a
49 * new method or constructor that is later added to one of the super
50 * classes), because the design and implementation of those methods
51 * are unlikely to have considered security impact with regard to
52 * CipherOutputStream.
53 *
54 * @author  Li Gong
55 * @see     java.io.OutputStream
56 * @see     java.io.FilterOutputStream
57 * @see     javax.crypto.Cipher
58 * @see     javax.crypto.CipherInputStream
59 *
60 * @since 1.4
61 */
62
63public class CipherOutputStream extends FilterOutputStream {
64
65    // the cipher engine to use to process stream data
66    private Cipher cipher;
67
68    // the underlying output stream
69    private OutputStream output;
70
71    /* the buffer holding one byte of incoming data */
72    private byte[] ibuffer = new byte[1];
73
74    // the buffer holding data ready to be written out
75    private byte[] obuffer;
76
77    // stream status
78    private boolean closed = false;
79
80    /**
81     *
82     * Constructs a CipherOutputStream from an OutputStream and a
83     * Cipher.
84     * <br>Note: if the specified output stream or cipher is
85     * null, a NullPointerException may be thrown later when
86     * they are used.
87     *
88     * @param os  the OutputStream object
89     * @param c   an initialized Cipher object
90     */
91    public CipherOutputStream(OutputStream os, Cipher c) {
92        super(os);
93        output = os;
94        cipher = c;
95    };
96
97    /**
98     * Constructs a CipherOutputStream from an OutputStream without
99     * specifying a Cipher. This has the effect of constructing a
100     * CipherOutputStream using a NullCipher.
101     * <br>Note: if the specified output stream is null, a
102     * NullPointerException may be thrown later when it is used.
103     *
104     * @param os  the OutputStream object
105     */
106    protected CipherOutputStream(OutputStream os) {
107        super(os);
108        output = os;
109        cipher = new NullCipher();
110    }
111
112    /**
113     * Writes the specified byte to this output stream.
114     *
115     * @param      b   the <code>byte</code>.
116     * @exception  IOException  if an I/O error occurs.
117     * @since      JCE1.2
118     */
119    public void write(int b) throws IOException {
120        ibuffer[0] = (byte) b;
121        obuffer = cipher.update(ibuffer, 0, 1);
122        if (obuffer != null) {
123            output.write(obuffer);
124            obuffer = null;
125        }
126    };
127
128    /**
129     * Writes <code>b.length</code> bytes from the specified byte array
130     * to this output stream.
131     * <p>
132     * The <code>write</code> method of
133     * <code>CipherOutputStream</code> calls the <code>write</code>
134     * method of three arguments with the three arguments
135     * <code>b</code>, <code>0</code>, and <code>b.length</code>.
136     *
137     * @param      b   the data.
138     * @exception  NullPointerException if <code>b</code> is null.
139     * @exception  IOException  if an I/O error occurs.
140     * @see        javax.crypto.CipherOutputStream#write(byte[], int, int)
141     * @since JCE1.2
142     */
143    public void write(byte b[]) throws IOException {
144        write(b, 0, b.length);
145    }
146
147    /**
148     * Writes <code>len</code> bytes from the specified byte array
149     * starting at offset <code>off</code> to this output stream.
150     *
151     * @param      b     the data.
152     * @param      off   the start offset in the data.
153     * @param      len   the number of bytes to write.
154     * @exception  IOException  if an I/O error occurs.
155     * @since      JCE1.2
156     */
157    public void write(byte b[], int off, int len) throws IOException {
158        obuffer = cipher.update(b, off, len);
159        if (obuffer != null) {
160            output.write(obuffer);
161            obuffer = null;
162        }
163    }
164
165    /**
166     * Flushes this output stream by forcing any buffered output bytes
167     * that have already been processed by the encapsulated cipher object
168     * to be written out.
169     *
170     * <p>Any bytes buffered by the encapsulated cipher
171     * and waiting to be processed by it will not be written out. For example,
172     * if the encapsulated cipher is a block cipher, and the total number of
173     * bytes written using one of the <code>write</code> methods is less than
174     * the cipher's block size, no bytes will be written out.
175     *
176     * @exception  IOException  if an I/O error occurs.
177     * @since      JCE1.2
178     */
179    public void flush() throws IOException {
180        if (obuffer != null) {
181            output.write(obuffer);
182            obuffer = null;
183        }
184        output.flush();
185    }
186
187    /**
188     * Closes this output stream and releases any system resources
189     * associated with this stream.
190     * <p>
191     * This method invokes the <code>doFinal</code> method of the encapsulated
192     * cipher object, which causes any bytes buffered by the encapsulated
193     * cipher to be processed. The result is written out by calling the
194     * <code>flush</code> method of this output stream.
195     * <p>
196     * This method resets the encapsulated cipher object to its initial state
197     * and calls the <code>close</code> method of the underlying output
198     * stream.
199     *
200     * @exception  IOException  if an I/O error occurs.
201     * @since      JCE1.2
202     */
203    public void close() throws IOException {
204        if (closed) {
205            return;
206        }
207
208        closed = true;
209        try {
210            obuffer = cipher.doFinal();
211        } catch (IllegalBlockSizeException | BadPaddingException e) {
212            obuffer = null;
213            // Android-added: Throw an exception when the underlying cipher does.  http://b/36636576
214            throw new IOException(e);
215        }
216        try {
217            flush();
218        } catch (IOException ignored) {}
219        out.close();
220    }
221}
222