Main Page | Packages | Class Hierarchy | Class List | Directories | File List | Class Members | Related Pages

EncryptedOutputStream.java

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     }

Generated on Wed Dec 14 21:05:32 2005 for OpenMobileIS by  doxygen 1.4.4