00001 // EncryptedOutputStream - an OutputStream that supports encryption 00002 // 00003 // Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved. 00004 // 00005 // Redistribution and use in source and binary forms, with or without 00006 // modification, are permitted provided that the following conditions 00007 // are met: 00008 // 1. Redistributions of source code must retain the above copyright 00009 // notice, this list of conditions and the following disclaimer. 00010 // 2. Redistributions in binary form must reproduce the above copyright 00011 // notice, this list of conditions and the following disclaimer in the 00012 // documentation and/or other materials provided with the distribution. 00013 // 00014 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 00015 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00016 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00017 // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 00018 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00019 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00020 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00021 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00022 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00023 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00024 // SUCH DAMAGE. 00025 // 00026 // Visit the ACME Labs Java page for up-to-date versions of this and other 00027 // fine Java utilities: http://www.acme.com/java/ 00028 00029 package Acme.Crypto; 00030 00031 import java.io.*; 00032 00034 // <P> 00035 // This class encapsulates a StreamCipher or BlockCipher as an OutputStream. 00036 // You set up your cipher, pass it and the underlying stream to the 00037 // EncryptedOutputStream constructor, and then write your cleartext to 00038 // this stream. It gets encrypted and sent to the underlying stream. 00039 // Decryption is done by an EncryptedInputStream. 00040 // <P> 00041 // When used with a StreamCipher, no output protocol is necessary, each 00042 // byte of cleartext turns into one byte of ciphertext. When used with a 00043 // BlockCipher it's more complicated. First, the raw BlockCipher gets 00044 // encapsulated into a CbcBlockCipher, which needs an initialization 00045 // vector; so each encrypted stream automatically starts off with such 00046 // a vector. After that, the stream is a series of (block,bytecount) 00047 // pairs. Each block of cleartext is encrypted into a block of ciphertext, 00048 // sent to the stream, and then one more byte is sent that says how 00049 // many bytes in the block are valid. Generally the bytecount will 00050 // be equal to the block size, but it can be less if the stream gets 00051 // flushed or closed on a partial block. 00052 // <P> 00053 // <A HREF="/resources/classes/Acme/Crypto/EncryptedOutputStream.java">Fetch the software.</A><BR> 00054 // <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A> 00055 // <P> 00056 // @see EncryptedInputStream 00057 // @see StreamCipher 00058 // @see BlockCipher 00059 // @see CbcBlockCipher 00060 00061 public class EncryptedOutputStream extends FilterOutputStream 00062 { 00063 00064 // The basic block cipher to use. 00065 private BlockCipher blockCipher = null; 00066 00067 // The stream cipher to use. 00068 private StreamCipher streamCipher = null; 00069 00070 // The cipher to use. 00071 private Cipher cipher; 00072 00073 // The CBC block cipher to use. 00074 private CbcBlockCipher cbcBlockCipher; 00075 00076 // Number of bytes in a block. 00077 private int blockSize; 00078 00079 // Number of bytes available for ciphertext in a block. 00080 private int cryptoSize; 00081 00082 // Block of bytes to be encrypted. 00083 private byte[] clearText; 00084 00085 // Block of bytes that have been encrypted. 00086 private byte[] cipherText; 00087 00088 // How many valid bytes are in the clearText block. 00089 private int byteCount; 00090 00092 // @param blockCipher The cipher to use, e.g. DesCipher, IdeaCipher 00093 // @param out The raw output stream that we will be encrypting to. 00094 public EncryptedOutputStream( BlockCipher blockCipher, OutputStream out ) throws IOException 00095 { 00096 super( out ); 00097 this.blockCipher = blockCipher; 00098 this.blockSize = blockCipher.blockSize(); 00099 cbcBlockCipher = new CbcBlockCipher( blockCipher ); 00100 cryptoSize = blockSize; 00101 clearText = new byte[blockSize]; 00102 cipherText = new byte[blockSize]; 00103 byteCount = 0; 00104 this.cipher = blockCipher; 00105 // Set a random IV and send it. 00106 out.write( cbcBlockCipher.setRandomIv(), 0, blockSize ); 00107 } 00108 00110 // @param streamCipher The cipher to use, e.g. Rc4Cipher, Rot13Cipher 00111 // @param out The raw output stream that we will be encrypting to. 00112 public EncryptedOutputStream( StreamCipher streamCipher, OutputStream out ) 00113 { 00114 super( out ); 00115 this.streamCipher = streamCipher; 00116 this.blockSize = 1; 00117 this.cipher = streamCipher; 00118 } 00119 00120 00122 public void setKey( String keyStr ) 00123 { 00124 cipher.setKey( keyStr ); 00125 } 00126 00127 00128 // Whether we are currently encrypting output or not. 00129 private boolean encrypting = true; 00130 00132 public void setEncrypting( boolean encrypting ) throws IOException 00133 { 00134 if ( this.encrypting && ! encrypting ) 00135 flush(); 00136 this.encrypting = encrypting; 00137 } 00138 00139 00140 private void sendBlock() throws IOException 00141 { 00142 // Fill up the block with random bytes, if necessary. 00143 for ( int i = byteCount; i < cryptoSize; ++i ) 00144 clearText[i] = (byte) ( Math.random() * 256.0 ); 00145 // Encrypt it. 00146 cbcBlockCipher.encrypt( clearText, 0, cipherText, 0 ); 00147 // Send the block. 00148 out.write( cipherText, 0, blockSize ); 00149 // Write out a count of valid bytes. 00150 out.write( (byte) byteCount ); 00151 byteCount = 0; 00152 } 00153 00155 public void write( int b ) throws IOException 00156 { 00157 if ( encrypting ) 00158 { 00159 if ( blockCipher != null ) 00160 { 00161 clearText[byteCount++] = (byte) b; 00162 if ( byteCount >= cryptoSize ) 00163 sendBlock(); 00164 } 00165 else 00166 // Stream cipher. 00167 out.write( streamCipher.encrypt( (byte) b ) ); 00168 } 00169 else 00170 // Not encrypting. 00171 out.write( b ); 00172 } 00173 00175 public void write( byte b[], int off, int len ) throws IOException 00176 { 00177 if ( encrypting ) 00178 { 00179 if ( blockCipher != null ) 00180 { 00181 for ( int i = off; i < off + len; ++i ) 00182 { 00183 clearText[byteCount++] = b[i]; 00184 if ( byteCount >= cryptoSize ) 00185 sendBlock(); 00186 } 00187 } 00188 else 00189 { 00190 // Stream cipher. 00191 byte[] cipherText = new byte[len]; 00192 streamCipher.encrypt( b, off, cipherText, 0, len ); 00193 out.write( cipherText, 0, len ); 00194 } 00195 } 00196 else 00197 // Not encrypting. 00198 out.write( b, off, len ); 00199 } 00200 00201 00203 public void flush() throws IOException 00204 { 00205 if ( encrypting && blockCipher != null && byteCount != 0 ) 00206 sendBlock(); 00207 out.flush(); 00208 } 00209 00210 }