1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.crypto.stream;
20
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.nio.ByteBuffer;
24 import java.nio.channels.WritableByteChannel;
25 import java.security.GeneralSecurityException;
26 import java.security.Key;
27 import java.security.spec.AlgorithmParameterSpec;
28 import java.util.Objects;
29 import java.util.Properties;
30
31 import javax.crypto.Cipher;
32 import javax.crypto.ShortBufferException;
33 import javax.crypto.spec.IvParameterSpec;
34
35 import org.apache.commons.crypto.cipher.CryptoCipher;
36 import org.apache.commons.crypto.stream.output.ChannelOutput;
37 import org.apache.commons.crypto.stream.output.Output;
38 import org.apache.commons.crypto.stream.output.StreamOutput;
39 import org.apache.commons.crypto.utils.Utils;
40
41
42
43
44
45
46
47
48
49
50
51 public class CryptoOutputStream extends OutputStream implements
52 WritableByteChannel {
53 private final byte[] oneByteBuf = new byte[1];
54
55
56 final Output output;
57
58
59 final CryptoCipher cipher;
60
61
62 private final int bufferSize;
63
64
65 final Key key;
66
67
68 private final AlgorithmParameterSpec params;
69
70
71 private boolean closed;
72
73
74
75
76
77 ByteBuffer inBuffer;
78
79
80
81
82
83 ByteBuffer outBuffer;
84
85
86
87
88
89
90
91
92
93
94
95 protected CryptoOutputStream(final Output output, final CryptoCipher cipher,
96 final int bufferSize, final Key key, final AlgorithmParameterSpec params)
97 throws IOException {
98
99 this.output = output;
100 this.bufferSize = CryptoInputStream.checkBufferSize(cipher, bufferSize);
101 this.cipher = cipher;
102
103 this.key = key;
104 this.params = params;
105
106 if (!(params instanceof IvParameterSpec)) {
107
108
109 throw new IOException("Illegal parameters");
110 }
111
112 inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
113 outBuffer = ByteBuffer.allocateDirect(this.bufferSize
114 + cipher.getBlockSize());
115
116 initCipher();
117 }
118
119
120
121
122
123
124
125
126
127
128
129 @SuppressWarnings("resource")
130 protected CryptoOutputStream(final OutputStream outputStream, final CryptoCipher cipher,
131 final int bufferSize, final Key key, final AlgorithmParameterSpec params)
132 throws IOException {
133 this(new StreamOutput(outputStream, bufferSize), cipher, bufferSize, key, params);
134 }
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150 @SuppressWarnings("resource")
151 public CryptoOutputStream(final String transformation,
152 final Properties properties, final OutputStream outputStream, final Key key,
153 final AlgorithmParameterSpec params) throws IOException {
154 this(outputStream, Utils.getCipherInstance(transformation, properties),
155 CryptoInputStream.getBufferSize(properties), key, params);
156
157 }
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173 @SuppressWarnings("resource")
174 public CryptoOutputStream(final String transformation,
175 final Properties properties, final WritableByteChannel out, final Key key,
176 final AlgorithmParameterSpec params) throws IOException {
177 this(out, Utils.getCipherInstance(transformation, properties), CryptoInputStream
178 .getBufferSize(properties), key, params);
179
180 }
181
182
183
184
185
186
187
188
189
190
191
192 @SuppressWarnings("resource")
193 protected CryptoOutputStream(final WritableByteChannel channel, final CryptoCipher cipher,
194 final int bufferSize, final Key key, final AlgorithmParameterSpec params)
195 throws IOException {
196 this(new ChannelOutput(channel), cipher, bufferSize, key, params);
197 }
198
199
200
201
202
203
204 protected void checkStream() throws IOException {
205 if (closed) {
206 throw new IOException("Stream closed");
207 }
208 }
209
210
211
212
213
214
215
216 @Override
217 public void close() throws IOException {
218 if (closed) {
219 return;
220 }
221
222 try {
223 encryptFinal();
224 output.close();
225 freeBuffers();
226 cipher.close();
227 super.close();
228 } finally {
229 closed = true;
230 }
231 }
232
233
234
235
236
237
238
239 protected void encrypt() throws IOException {
240
241 inBuffer.flip();
242 outBuffer.clear();
243
244 try {
245 cipher.update(inBuffer, outBuffer);
246 } catch (final ShortBufferException e) {
247 throw new IOException(e);
248 }
249
250 inBuffer.clear();
251 outBuffer.flip();
252
253
254 while (outBuffer.hasRemaining()) {
255 output.write(outBuffer);
256 }
257 }
258
259
260
261
262
263
264 protected void encryptFinal() throws IOException {
265 inBuffer.flip();
266 outBuffer.clear();
267
268 try {
269 cipher.doFinal(inBuffer, outBuffer);
270 } catch (final GeneralSecurityException e) {
271 throw new IOException(e);
272 }
273
274 inBuffer.clear();
275 outBuffer.flip();
276
277
278 while (outBuffer.hasRemaining()) {
279 output.write(outBuffer);
280 }
281 }
282
283
284
285
286
287
288
289
290 @Override
291 public void flush() throws IOException {
292 checkStream();
293 encrypt();
294 output.flush();
295 super.flush();
296 }
297
298
299 protected void freeBuffers() {
300 CryptoInputStream.freeDirectBuffer(inBuffer);
301 CryptoInputStream.freeDirectBuffer(outBuffer);
302 }
303
304
305
306
307
308
309 protected int getBufferSize() {
310 return bufferSize;
311 }
312
313
314
315
316
317
318 protected CryptoCipher getCipher() {
319 return cipher;
320 }
321
322
323
324
325
326
327 protected ByteBuffer getInBuffer() {
328 return inBuffer;
329 }
330
331
332
333
334
335
336 protected ByteBuffer getOutBuffer() {
337 return outBuffer;
338 }
339
340
341
342
343
344
345 protected void initCipher() throws IOException {
346 try {
347 cipher.init(Cipher.ENCRYPT_MODE, key, params);
348 } catch (final GeneralSecurityException e) {
349 throw new IOException(e);
350 }
351 }
352
353
354
355
356
357
358
359 @Override
360 public boolean isOpen() {
361 return !closed;
362 }
363
364
365
366
367
368
369
370
371
372
373
374
375 @Override
376 public void write(final byte[] array, int off, int len) throws IOException {
377 checkStream();
378 Objects.requireNonNull(array, "array");
379 final int arrayLength = array.length;
380 if (off < 0 || len < 0 || off > arrayLength || len > arrayLength - off) {
381 throw new IndexOutOfBoundsException();
382 }
383
384 while (len > 0) {
385 final int remaining = inBuffer.remaining();
386 if (len < remaining) {
387 inBuffer.put(array, off, len);
388 len = 0;
389 } else {
390 inBuffer.put(array, off, remaining);
391 off += remaining;
392 len -= remaining;
393 encrypt();
394 }
395 }
396 }
397
398
399
400
401
402
403
404
405
406
407 @Override
408 public int write(final ByteBuffer src) throws IOException {
409 checkStream();
410 final int len = src.remaining();
411 int remaining = len;
412 while (remaining > 0) {
413 final int space = inBuffer.remaining();
414 if (remaining < space) {
415 inBuffer.put(src);
416 remaining = 0;
417 } else {
418
419 final int oldLimit = src.limit();
420 final int newLimit = src.position() + space;
421 src.limit(newLimit);
422
423 inBuffer.put(src);
424
425
426 src.limit(oldLimit);
427
428 remaining -= space;
429 encrypt();
430 }
431 }
432
433 return len;
434 }
435
436
437
438
439
440
441
442
443 @Override
444 public void write(final int b) throws IOException {
445 oneByteBuf[0] = (byte) (b & 0xff);
446 write(oneByteBuf, 0, oneByteBuf.length);
447 }
448 }