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