KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > Acme > Crypto > EncryptedInputStream


1 // EncryptedInputStream - an InputStream that supports encryption
2
//
3
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
4
//
5
// Redistribution and use in source and binary forms, with or without
6
// modification, are permitted provided that the following conditions
7
// are met:
8
// 1. Redistributions of source code must retain the above copyright
9
// notice, this list of conditions and the following disclaimer.
10
// 2. Redistributions in binary form must reproduce the above copyright
11
// notice, this list of conditions and the following disclaimer in the
12
// documentation and/or other materials provided with the distribution.
13
//
14
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
// SUCH DAMAGE.
25
//
26
// Visit the ACME Labs Java page for up-to-date versions of this and other
27
// fine Java utilities: http://www.acme.com/java/
28

29 package Acme.Crypto;
30
31 import java.io.*;
32
33 /// An InputStream that supports encryption.
34
// <P>
35
// This class encapsulates a StreamCipher or BlockCipher as an InputStream.
36
// You set up your cipher, pass it and the underlying stream to the
37
// EncryptedInputStream constructor, and then read your cleartext from
38
// this stream. It gets read from the underlying stream and decrypted.
39
// Encryption is done by an EncryptedOutputStream.
40
// <P>
41
// When used with a StreamCipher, no input protocol is necessary, each
42
// byte of ciphertext turns into one byte of cleartext. When used with a
43
// BlockCipher it's more complicated. First, the raw BlockCipher gets
44
// encapsulated into a CbcBlockCipher, which needs an initialization
45
// vector; so each encrypted stream automatically starts off with such
46
// a vector. After that, the stream is a series of (block,bytecount)
47
// pairs. Each block of ciphertext is read from the stream, decrypted
48
// into a block of cleartext, and then one more byte is read that says how
49
// many bytes in the block are valid. Generally the bytecount will
50
// be equal to the block size, but it can be less if the stream gets
51
// flushed or closed on a partial block.
52
// <P>
53
// <A HREF="../../../resources/classes/Acme/Crypto/EncryptedInputStream.java">Fetch the software.</A><BR>
54
// <A HREF="../../../resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
55
// <P>
56
// @see EncryptedOutputStream
57
// @see StreamCipher
58
// @see BlockCipher
59
// @see CbcBlockCipher
60

61 public class EncryptedInputStream extends FilterInputStream
62     {
63
64     // The basic block cipher to use.
65
private BlockCipher blockCipher = null;
66
67     // The stream cipher to use.
68
private StreamCipher streamCipher = null;
69
70     // The cipher to use.
71
private Cipher cipher;
72
73     // The CBC block cipher to use.
74
private CbcBlockCipher cbcBlockCipher = null;
75
76     // Number of bytes in a block.
77
private int blockSize;
78
79     // Number of bytes available for ciphertext in a block.
80
private int cryptoSize;
81
82     // Block of bytes to be decrypted.
83
private byte[] cipherText;
84
85     // Block of bytes that have been decrypted.
86
private byte[] clearText;
87
88     // How many valid bytes are in the cipherText block.
89
private int byteCount;
90
91     // How many decrypted bytes have been read.
92
private int bytesRead;
93
94     /// Constructor for block ciphers.
95
// @param blockCipher The cipher to use, e.g. DesCipher, IdeaCipher
96
// @param in The raw input stream that we will be decrypting.
97
public EncryptedInputStream( BlockCipher blockCipher, InputStream in )
98     {
99     super( in );
100     this.blockCipher = blockCipher;
101     this.blockSize = blockCipher.blockSize();
102     cbcBlockCipher = new CbcBlockCipher( blockCipher );
103     this.cryptoSize = blockSize;
104     cipherText = new byte[blockSize];
105     clearText = new byte[blockSize];
106     byteCount = 0;
107     bytesRead = 0;
108     this.cipher = blockCipher;
109     }
110
111     /// Constructor for stream ciphers.
112
// @param streamCipher The cipher to use, e.g. Rc4Cipher, Rot13Cipher
113
// @param in The raw input stream that we will be decrypting.
114
public EncryptedInputStream( StreamCipher streamCipher, InputStream in )
115     {
116     super( in );
117     this.streamCipher = streamCipher;
118     this.cipher = streamCipher;
119     }
120     
121
122     private boolean inited = false;
123
124     private void init() throws IOException
125     {
126     if ( ! inited )
127         {
128         inited = true;
129         if ( blockCipher != null )
130         {
131         // Read the IV from the stream and set it.
132
byte[] iv = new byte[blockSize];
133         int r = Acme.Utils.read( in, iv, 0, blockSize );
134         if ( r == -1 || r != blockSize )
135             throw new IOException( "truncated initialization vector" );
136         cbcBlockCipher.setIv( iv );
137         }
138         }
139     }
140     
141
142     /// Set the key.
143
public void setKey( String JavaDoc keyStr )
144     {
145     cipher.setKey( keyStr );
146     }
147
148
149     // Whether we are currently decrypting input or not.
150
private boolean decrypting = true;
151
152     /// Decrypting can be enabled or disabled temporarily.
153
public void setDecrypting( boolean decrypting ) throws IOException
154     {
155     if ( this.decrypting && ! decrypting )
156         {
157         // !!! do something about unread decrypted bytes?
158
}
159     this.decrypting = decrypting;
160     }
161
162
163     // Read an encrypted block. Returns -1 on EOF.
164
private int getBlock() throws IOException
165     {
166     int r = Acme.Utils.read( in, cipherText, 0, blockSize );
167     if ( r == -1 )
168         return -1;
169     if ( r != blockSize )
170         throw new IOException( "truncated ciphertext block" );
171     // Decrypt the block.
172
cbcBlockCipher.decrypt( cipherText, 0, clearText, 0 );
173     // Get the byte count.
174
byteCount = in.read();
175     if ( byteCount == -1 )
176         throw new IOException( "missing ciphertext bytecount" );
177     if ( byteCount == 0 || byteCount > cryptoSize )
178         throw new IOException( "invalid ciphertext bytecount" );
179     bytesRead = 0;
180     return byteCount;
181     }
182
183     /// Read a byte of data.
184
// @return -1 on EOF.
185
public int read() throws IOException
186     {
187     init();
188     if ( decrypting )
189         {
190         if ( blockCipher != null )
191         {
192         if ( bytesRead >= byteCount )
193             if ( getBlock() == -1 )
194             return -1;
195         return clearText[bytesRead++] & 0xff;
196         }
197         else
198         {
199         // Stream cipher.
200
int r = in.read();
201         if ( r == -1 )
202             return -1;
203         return streamCipher.decrypt( (byte) r ) & 0xff;
204         }
205         }
206     else
207         // Not decrypting.
208
return in.read();
209     }
210
211     /// Read into an array of bytes. This is a fixed version
212
// of java.io.InputStream.read(byte[], int, int). The
213
// standard version catches and ignores IOExceptions from
214
// below; this version sends them on to the caller.
215
public int read( byte[] b, int off, int len ) throws IOException
216     {
217     init();
218     if ( decrypting )
219         {
220         if ( blockCipher != null )
221         // It would be tricky to optimize this to decrypt whole blocks.
222
return Acme.Utils.read( this, b, off, len );
223         else
224         {
225         // Stream cipher.
226
byte[] cipherText = new byte[len];
227         int r = Acme.Utils.read( in, cipherText, 0, len );
228         if ( r == -1 )
229             return -1;
230         streamCipher.decrypt( cipherText, 0, b, off, r );
231         return r;
232         }
233         }
234     else
235         // Not decrypting.
236
return Acme.Utils.read( in, b, off, len );
237     }
238
239     }
240
Popular Tags