KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > schlichtherle > crypto > io > CipherOutputStream


1 /*
2  * CipherOutputStream.java
3  *
4  * Created on 12. Oktober 2005, 12:32
5  */

6 /*
7  * Copyright 2005 Schlichtherle IT Services
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */

21
22 package de.schlichtherle.crypto.io;
23
24 import java.io.FilterOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.OutputStream JavaDoc;
27
28 import org.bouncycastle.crypto.BufferedBlockCipher;
29 import org.bouncycastle.crypto.InvalidCipherTextException;
30
31 /**
32  * Similar to <code>javax.crypto.CipherOutputStream</code>,
33  * with some exceptions:
34  * <ul>
35  * <li>This implementation is based on Bouncy Castle's lightweight crypto API
36  * and uses a {@link BufferedBlockCipher} for ciphering.
37  * <li>The {@link #cipher} used for encryption or decryption is accessible to
38  * subclasses.
39  * <li>The <tt>flush()</tt> method just flushes the underlying output stream
40  * and has no effect on the cipher.
41  * <li>A {@link #finish()} method has been added to allow finishing the output
42  * (probably producing padding bytes) without closing the output.
43  * This could be used in a subclass to produce a trailer with additional
44  * information about the ciphered data (e.g. a MAC).
45  * </ul>
46  *
47  * @author Christian Schlichtherle
48  */

49 public class CipherOutputStream extends FilterOutputStream JavaDoc {
50
51     /** The buffered block cipher used for preprocessing the output. */
52     protected BufferedBlockCipher cipher;
53     
54     /**
55      * The buffer used for preprocessing the output.
56      * This buffer is autosized to the largest buffer written to this stream.
57      */

58     private byte[] outBuf = new byte[0];
59
60     /** Whether this stream has been closed or not. */
61     private boolean closed;
62
63     /**
64      * Creates a new instance of CipherOutputStream.
65      * Please note that unlike <code>javax.crypto.CipherOutputStream</code>,
66      * the cipher does not need to be initialized before calling this
67      * constructor.
68      * However, the cipher must be initialized before anything is actually
69      * written to this stream or before this stream is closed.
70      *
71      * @param out The output stream to write the encrypted or decrypted data to.
72      * Maybe <tt>null</tt> if initialized by the subclass constructor.
73      * @param cipher The cipher to use for encryption or decryption.
74      * Maybe <tt>null</tt> for subsequent initialization by a subclass.
75      */

76     public CipherOutputStream(OutputStream JavaDoc out, BufferedBlockCipher cipher) {
77         super(out);
78         
79         this.cipher = cipher;
80     }
81
82     /**
83      * Ciphers and writes the given byte to the underlying output stream.
84      *
85      * @param b The byte to cipher and write.
86      *
87      * @throws IOException If out or cipher aren't properly initialized,
88      * the stream has been closed or an I/O error occured.
89      */

90     public void write(final int b)
91     throws IOException JavaDoc {
92         ensureInit();
93
94         int outLen = cipher.getUpdateOutputSize(1);
95         if (outLen > outBuf.length)
96             outBuf = new byte[outLen];
97         outLen = cipher.processByte((byte) b, outBuf, 0);
98         if (outLen > 0)
99             out.write(outBuf, 0, outLen);
100     }
101
102     /**
103      * Ciphers and writes the contents of the given byte array to the
104      * underlying output stream.
105      *
106      * @param buf The buffer holding the data to cipher and write.
107      * @param off The start offset in the data buffer.
108      * @param len The number of bytes to cipher and write.
109      *
110      * @throws IOException If out or cipher aren't properly initialized,
111      * the stream has been closed or an I/O error occured.
112      */

113     public void write(final byte[] buf, final int off, final int len)
114     throws IOException JavaDoc {
115         ensureInit();
116
117         int outLen = cipher.getUpdateOutputSize(len);
118         if (outLen > outBuf.length)
119             outBuf = new byte[outLen];
120         outLen = cipher.processBytes(buf, off, len, outBuf, 0);
121         out.write(outBuf, 0, outLen);
122     }
123
124     /**
125      * Finishes this stream and resets it to it's initial state.
126      * Calling this method causes all remaining buffered bytes to be written,
127      * padding to be added if necessary and the underlying output stream to
128      * get flushed.
129      * <p>
130      * Please note that subsequent calls to any write operations after this
131      * method may cause an error in the output data if padding is used!
132      *
133      * @throws IOException If out or cipher aren't properly initialized,
134      * the stream has been closed, an I/O error occured the cipher
135      * text is invalid, i.e. required padding information is missing.
136      */

137     public void finish() throws IOException JavaDoc {
138         ensureInit();
139
140         int outLen = cipher.getOutputSize(0);
141         if (outLen > outBuf.length)
142             outBuf = new byte[outLen];
143         try {
144             outLen = cipher.doFinal(outBuf, 0);
145         } catch (InvalidCipherTextException icte) {
146             IOException JavaDoc ioe = new IOException JavaDoc(icte.toString());
147             ioe.initCause(icte);
148             throw ioe;
149         }
150         out.write(outBuf, 0, outLen);
151         out.flush();
152         //outBuf = new byte[0];
153
}
154
155     /**
156      * Ensures that this stream is open and has been initialized.
157      *
158      * @throws IOException If the preconditions do not hold.
159      */

160     private final void ensureInit() throws IOException JavaDoc {
161         if (cipher == null)
162             throw new IOException JavaDoc("CipherOutputStream has already been closed or is not initialized!");
163     }
164
165     /**
166      * Closes this output stream and releases any resources associated with it.
167      * This method calls {@link #finish()} and then closes and nullifies
168      * the underlying output stream {@link #out} and the cipher {@link #cipher}.
169      *
170      * @throws IOException If an I/O error occurs.
171      */

172     public void close() throws IOException JavaDoc {
173         // Order is important here!
174
if (!closed) {
175             closed = true;
176             try {
177                 finish();
178             } finally {
179                 cipher = null;
180                 super.close();
181             }
182         }
183     }
184 }
185
Popular Tags