001: // EncryptedOutputStream - an OutputStream that supports encryption
002: //
003: // Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
004: //
005: // Redistribution and use in source and binary forms, with or without
006: // modification, are permitted provided that the following conditions
007: // are met:
008: // 1. Redistributions of source code must retain the above copyright
009: // notice, this list of conditions and the following disclaimer.
010: // 2. Redistributions in binary form must reproduce the above copyright
011: // notice, this list of conditions and the following disclaimer in the
012: // documentation and/or other materials provided with the distribution.
013: //
014: // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
015: // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
016: // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
017: // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
018: // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
019: // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
020: // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
021: // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
022: // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
023: // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
024: // SUCH DAMAGE.
025: //
026: // Visit the ACME Labs Java page for up-to-date versions of this and other
027: // fine Java utilities: http://www.acme.com/java/
028:
029: package Acme.Crypto;
030:
031: import java.io.*;
032:
033: /// An OutputStream that supports encryption.
034: // <P>
035: // This class encapsulates a StreamCipher or BlockCipher as an OutputStream.
036: // You set up your cipher, pass it and the underlying stream to the
037: // EncryptedOutputStream constructor, and then write your cleartext to
038: // this stream. It gets encrypted and sent to the underlying stream.
039: // Decryption is done by an EncryptedInputStream.
040: // <P>
041: // When used with a StreamCipher, no output protocol is necessary, each
042: // byte of cleartext turns into one byte of ciphertext. When used with a
043: // BlockCipher it's more complicated. First, the raw BlockCipher gets
044: // encapsulated into a CbcBlockCipher, which needs an initialization
045: // vector; so each encrypted stream automatically starts off with such
046: // a vector. After that, the stream is a series of (block,bytecount)
047: // pairs. Each block of cleartext is encrypted into a block of ciphertext,
048: // sent to the stream, and then one more byte is sent that says how
049: // many bytes in the block are valid. Generally the bytecount will
050: // be equal to the block size, but it can be less if the stream gets
051: // flushed or closed on a partial block.
052: // <P>
053: // <A HREF="/resources/classes/Acme/Crypto/EncryptedOutputStream.java">Fetch the software.</A><BR>
054: // <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
055: // <P>
056: // @see EncryptedInputStream
057: // @see StreamCipher
058: // @see BlockCipher
059: // @see CbcBlockCipher
060:
061: public class EncryptedOutputStream extends FilterOutputStream {
062:
063: // The basic block cipher to use.
064: private BlockCipher blockCipher = null;
065:
066: // The stream cipher to use.
067: private StreamCipher streamCipher = null;
068:
069: // The cipher to use.
070: private Cipher cipher;
071:
072: // The CBC block cipher to use.
073: private CbcBlockCipher cbcBlockCipher;
074:
075: // Number of bytes in a block.
076: private int blockSize;
077:
078: // Number of bytes available for ciphertext in a block.
079: private int cryptoSize;
080:
081: // Block of bytes to be encrypted.
082: private byte[] clearText;
083:
084: // Block of bytes that have been encrypted.
085: private byte[] cipherText;
086:
087: // How many valid bytes are in the clearText block.
088: private int byteCount;
089:
090: /// Constructor for block ciphers.
091: // @param blockCipher The cipher to use, e.g. DesCipher, IdeaCipher
092: // @param out The raw output stream that we will be encrypting to.
093: public EncryptedOutputStream(BlockCipher blockCipher,
094: OutputStream out) throws IOException {
095: super (out);
096: this .blockCipher = blockCipher;
097: this .blockSize = blockCipher.blockSize();
098: cbcBlockCipher = new CbcBlockCipher(blockCipher);
099: cryptoSize = blockSize;
100: clearText = new byte[blockSize];
101: cipherText = new byte[blockSize];
102: byteCount = 0;
103: this .cipher = blockCipher;
104: // Set a random IV and send it.
105: out.write(cbcBlockCipher.setRandomIv(), 0, blockSize);
106: }
107:
108: /// Constructor for stream ciphers.
109: // @param streamCipher The cipher to use, e.g. Rc4Cipher, Rot13Cipher
110: // @param out The raw output stream that we will be encrypting to.
111: public EncryptedOutputStream(StreamCipher streamCipher,
112: OutputStream out) {
113: super (out);
114: this .streamCipher = streamCipher;
115: this .blockSize = 1;
116: this .cipher = streamCipher;
117: }
118:
119: /// Set the key.
120: public void setKey(String keyStr) {
121: cipher.setKey(keyStr);
122: }
123:
124: // Whether we are currently encrypting output or not.
125: private boolean encrypting = true;
126:
127: /// Encrypting can be enabled or disabled temporarily.
128: public void setEncrypting(boolean encrypting) throws IOException {
129: if (this .encrypting && !encrypting)
130: flush();
131: this .encrypting = encrypting;
132: }
133:
134: private void sendBlock() throws IOException {
135: // Fill up the block with random bytes, if necessary.
136: for (int i = byteCount; i < cryptoSize; ++i)
137: clearText[i] = (byte) (Math.random() * 256.0);
138: // Encrypt it.
139: cbcBlockCipher.encrypt(clearText, 0, cipherText, 0);
140: // Send the block.
141: out.write(cipherText, 0, blockSize);
142: // Write out a count of valid bytes.
143: out.write((byte) byteCount);
144: byteCount = 0;
145: }
146:
147: /// Write a byte.
148: public void write(int b) throws IOException {
149: if (encrypting) {
150: if (blockCipher != null) {
151: clearText[byteCount++] = (byte) b;
152: if (byteCount >= cryptoSize)
153: sendBlock();
154: } else
155: // Stream cipher.
156: out.write(streamCipher.encrypt((byte) b));
157: } else
158: // Not encrypting.
159: out.write(b);
160: }
161:
162: /// Write some bytes.
163: public void write(byte b[], int off, int len) throws IOException {
164: if (encrypting) {
165: if (blockCipher != null) {
166: for (int i = off; i < off + len; ++i) {
167: clearText[byteCount++] = b[i];
168: if (byteCount >= cryptoSize)
169: sendBlock();
170: }
171: } else {
172: // Stream cipher.
173: byte[] cipherText = new byte[len];
174: streamCipher.encrypt(b, off, cipherText, 0, len);
175: out.write(cipherText, 0, len);
176: }
177: } else
178: // Not encrypting.
179: out.write(b, off, len);
180: }
181:
182: /// Flush the stream. This will write any buffered output bytes.
183: public void flush() throws IOException {
184: if (encrypting && blockCipher != null && byteCount != 0)
185: sendBlock();
186: out.flush();
187: }
188:
189: }
|