001: // EncryptedInputStream - an InputStream 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 InputStream that supports encryption.
034: // <P>
035: // This class encapsulates a StreamCipher or BlockCipher as an InputStream.
036: // You set up your cipher, pass it and the underlying stream to the
037: // EncryptedInputStream constructor, and then read your cleartext from
038: // this stream. It gets read from the underlying stream and decrypted.
039: // Encryption is done by an EncryptedOutputStream.
040: // <P>
041: // When used with a StreamCipher, no input protocol is necessary, each
042: // byte of ciphertext turns into one byte of cleartext. 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 ciphertext is read from the stream, decrypted
048: // into a block of cleartext, and then one more byte is read 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/EncryptedInputStream.java">Fetch the software.</A><BR>
054: // <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
055: // <P>
056: // @see EncryptedOutputStream
057: // @see StreamCipher
058: // @see BlockCipher
059: // @see CbcBlockCipher
060:
061: public class EncryptedInputStream extends FilterInputStream {
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 = null;
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 decrypted.
082: private byte[] cipherText;
083:
084: // Block of bytes that have been decrypted.
085: private byte[] clearText;
086:
087: // How many valid bytes are in the cipherText block.
088: private int byteCount;
089:
090: // How many decrypted bytes have been read.
091: private int bytesRead;
092:
093: /// Constructor for block ciphers.
094: // @param blockCipher The cipher to use, e.g. DesCipher, IdeaCipher
095: // @param in The raw input stream that we will be decrypting.
096: public EncryptedInputStream(BlockCipher blockCipher, InputStream in) {
097: super (in);
098: this .blockCipher = blockCipher;
099: this .blockSize = blockCipher.blockSize();
100: cbcBlockCipher = new CbcBlockCipher(blockCipher);
101: this .cryptoSize = blockSize;
102: cipherText = new byte[blockSize];
103: clearText = new byte[blockSize];
104: byteCount = 0;
105: bytesRead = 0;
106: this .cipher = blockCipher;
107: }
108:
109: /// Constructor for stream ciphers.
110: // @param streamCipher The cipher to use, e.g. Rc4Cipher, Rot13Cipher
111: // @param in The raw input stream that we will be decrypting.
112: public EncryptedInputStream(StreamCipher streamCipher,
113: InputStream in) {
114: super (in);
115: this .streamCipher = streamCipher;
116: this .cipher = streamCipher;
117: }
118:
119: private boolean inited = false;
120:
121: private void init() throws IOException {
122: if (!inited) {
123: inited = true;
124: if (blockCipher != null) {
125: // Read the IV from the stream and set it.
126: byte[] iv = new byte[blockSize];
127: int r = Acme.Utils.read(in, iv, 0, blockSize);
128: if (r == -1 || r != blockSize)
129: throw new IOException(
130: "truncated initialization vector");
131: cbcBlockCipher.setIv(iv);
132: }
133: }
134: }
135:
136: /// Set the key.
137: public void setKey(String keyStr) {
138: cipher.setKey(keyStr);
139: }
140:
141: // Whether we are currently decrypting input or not.
142: private boolean decrypting = true;
143:
144: /// Decrypting can be enabled or disabled temporarily.
145: public void setDecrypting(boolean decrypting) throws IOException {
146: if (this .decrypting && !decrypting) {
147: // !!! do something about unread decrypted bytes?
148: }
149: this .decrypting = decrypting;
150: }
151:
152: // Read an encrypted block. Returns -1 on EOF.
153: private int getBlock() throws IOException {
154: int r = Acme.Utils.read(in, cipherText, 0, blockSize);
155: if (r == -1)
156: return -1;
157: if (r != blockSize)
158: throw new IOException("truncated ciphertext block");
159: // Decrypt the block.
160: cbcBlockCipher.decrypt(cipherText, 0, clearText, 0);
161: // Get the byte count.
162: byteCount = in.read();
163: if (byteCount == -1)
164: throw new IOException("missing ciphertext bytecount");
165: if (byteCount == 0 || byteCount > cryptoSize)
166: throw new IOException("invalid ciphertext bytecount");
167: bytesRead = 0;
168: return byteCount;
169: }
170:
171: /// Read a byte of data.
172: // @return -1 on EOF.
173: public int read() throws IOException {
174: init();
175: if (decrypting) {
176: if (blockCipher != null) {
177: if (bytesRead >= byteCount)
178: if (getBlock() == -1)
179: return -1;
180: return clearText[bytesRead++] & 0xff;
181: } else {
182: // Stream cipher.
183: int r = in.read();
184: if (r == -1)
185: return -1;
186: return streamCipher.decrypt((byte) r) & 0xff;
187: }
188: } else
189: // Not decrypting.
190: return in.read();
191: }
192:
193: /// Read into an array of bytes. This is a fixed version
194: // of java.io.InputStream.read(byte[], int, int). The
195: // standard version catches and ignores IOExceptions from
196: // below; this version sends them on to the caller.
197: public int read(byte[] b, int off, int len) throws IOException {
198: init();
199: if (decrypting) {
200: if (blockCipher != null)
201: // It would be tricky to optimize this to decrypt whole blocks.
202: return Acme.Utils.read(this , b, off, len);
203: else {
204: // Stream cipher.
205: byte[] cipherText = new byte[len];
206: int r = Acme.Utils.read(in, cipherText, 0, len);
207: if (r == -1)
208: return -1;
209: streamCipher.decrypt(cipherText, 0, b, off, r);
210: return r;
211: }
212: } else
213: // Not decrypting.
214: return Acme.Utils.read(in, b, off, len);
215: }
216:
217: }
|