GNU Classpath (0.20) | |
Frames | No Frames |
1: /* CipherOutputStream.java -- Filters output through a cipher. 2: Copyright (C) 2004 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.crypto; 40: 41: import java.io.FilterOutputStream; 42: import java.io.IOException; 43: import java.io.OutputStream; 44: 45: /** 46: * A filtered output stream that transforms data written to it with a 47: * {@link Cipher} before sending it to the underlying output stream. 48: * 49: * @author Casey Marshall (csm@gnu.org) 50: */ 51: public class CipherOutputStream extends FilterOutputStream 52: { 53: 54: // Fields. 55: // ------------------------------------------------------------------------ 56: 57: /** The underlying cipher. */ 58: private Cipher cipher; 59: 60: private byte[][] inBuffer; 61: 62: private int inLength; 63: 64: private byte[] outBuffer; 65: 66: private static final int FIRST_TIME = 0; 67: private static final int SECOND_TIME = 1; 68: private static final int SEASONED = 2; 69: private int state; 70: 71: /** True if the cipher is a stream cipher (blockSize == 1) */ 72: private boolean isStream; 73: 74: // Constructors. 75: // ------------------------------------------------------------------------ 76: 77: /** 78: * Create a new cipher output stream. The cipher argument must have 79: * already been initialized. 80: * 81: * @param out The sink for transformed data. 82: * @param cipher The cipher to transform data with. 83: */ 84: public CipherOutputStream(OutputStream out, Cipher cipher) 85: { 86: super(out); 87: if (cipher != null) 88: { 89: this.cipher = cipher; 90: if (!(isStream = cipher.getBlockSize() == 1)) 91: { 92: inBuffer = new byte[2][]; 93: inBuffer[0] = new byte[cipher.getBlockSize()]; 94: inBuffer[1] = new byte[cipher.getBlockSize()]; 95: inLength = 0; 96: state = FIRST_TIME; 97: } 98: } 99: else 100: this.cipher = new NullCipher(); 101: } 102: 103: /** 104: * Create a cipher output stream with no cipher. 105: * 106: * @param out The sink for transformed data. 107: */ 108: protected CipherOutputStream(OutputStream out) 109: { 110: super(out); 111: } 112: 113: // Instance methods. 114: // ------------------------------------------------------------------------ 115: 116: /** 117: * Close this output stream, and the sink output stream. 118: * 119: * <p>This method will first invoke the {@link Cipher#doFinal()} 120: * method of the underlying {@link Cipher}, and writes the output of 121: * that method to the sink output stream. 122: * 123: * @throws java.io.IOException If an I/O error occurs, or if an error 124: * is caused by finalizing the transformation. 125: */ 126: public void close() throws IOException 127: { 128: try 129: { 130: int len; 131: if (state != FIRST_TIME) 132: { 133: len = cipher.update(inBuffer[0], 0, inBuffer[0].length, outBuffer); 134: out.write(outBuffer, 0, len); 135: } 136: len = cipher.doFinal(inBuffer[0], 0, inLength, outBuffer); 137: out.write(outBuffer, 0, len); 138: } 139: catch (javax.crypto.IllegalBlockSizeException ibse) 140: { 141: throw new IOException(ibse.toString()); 142: } 143: catch (javax.crypto.BadPaddingException bpe) 144: { 145: throw new IOException(bpe.toString()); 146: } 147: catch (ShortBufferException sbe) 148: { 149: throw new IOException(sbe.toString()); 150: } 151: out.flush(); 152: out.close(); 153: } 154: 155: /** 156: * Flush any pending output. 157: * 158: * @throws java.io.IOException If an I/O error occurs. 159: */ 160: public void flush() throws IOException 161: { 162: out.flush(); 163: } 164: 165: /** 166: * Write a single byte to the output stream. 167: * 168: * @param b The next byte. 169: * @throws java.io.IOException If an I/O error occurs, or if the 170: * underlying cipher is not in the correct state to transform 171: * data. 172: */ 173: public void write(int b) throws IOException 174: { 175: if (isStream) 176: { 177: byte[] buf = new byte[] { (byte) b }; 178: try 179: { 180: cipher.update(buf, 0, 1, buf, 0); 181: } 182: catch (ShortBufferException sbe) 183: { 184: throw new IOException(sbe.toString()); 185: } 186: out.write(buf); 187: return; 188: } 189: inBuffer[1][inLength++] = (byte) b; 190: if (inLength == inBuffer[1].length) 191: process(); 192: } 193: 194: /** 195: * Write a byte array to the output stream. 196: * 197: * @param buf The next bytes. 198: * @throws java.io.IOException If an I/O error occurs, or if the 199: * underlying cipher is not in the correct state to transform 200: * data. 201: */ 202: public void write(byte[] buf) throws IOException 203: { 204: write(buf, 0, buf.length); 205: } 206: 207: /** 208: * Write a portion of a byte array to the output stream. 209: * 210: * @param buf The next bytes. 211: * @param off The offset in the byte array to start. 212: * @param len The number of bytes to write. 213: * @throws java.io.IOException If an I/O error occurs, or if the 214: * underlying cipher is not in the correct state to transform 215: * data. 216: */ 217: public void write(byte[] buf, int off, int len) throws IOException 218: { 219: if (isStream) 220: { 221: out.write(cipher.update(buf, off, len)); 222: return; 223: } 224: int count = 0; 225: while (count < len) 226: { 227: int l = Math.min(inBuffer[1].length - inLength, len - count); 228: System.arraycopy(buf, off+count, inBuffer[1], inLength, l); 229: count += l; 230: inLength += l; 231: if (inLength == inBuffer[1].length) 232: process(); 233: } 234: } 235: 236: // Own method. 237: // ------------------------------------------------------------------------- 238: 239: private void process() throws IOException 240: { 241: if (state == SECOND_TIME) 242: { 243: state = SEASONED; 244: } 245: else 246: { 247: byte[] temp = inBuffer[0]; 248: inBuffer[0] = inBuffer[1]; 249: inBuffer[1] = temp; 250: } 251: if (state == FIRST_TIME) 252: { 253: inLength = 0; 254: state = SECOND_TIME; 255: return; 256: } 257: try 258: { 259: cipher.update(inBuffer[0], 0, inBuffer[0].length, outBuffer); 260: } 261: catch (ShortBufferException sbe) 262: { 263: throw new IOException(sbe.toString()); 264: } 265: out.write(outBuffer); 266: inLength = 0; 267: } 268: }
GNU Classpath (0.20) |