1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.shiro.crypto.cipher;
20
21 import org.apache.shiro.crypto.CryptoException;
22 import org.apache.shiro.lang.util.ByteSource;
23 import org.apache.shiro.lang.util.StringUtils;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 import javax.crypto.CipherInputStream;
28 import javax.crypto.spec.IvParameterSpec;
29 import javax.crypto.spec.SecretKeySpec;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.security.Key;
34 import java.security.SecureRandom;
35 import java.security.spec.AlgorithmParameterSpec;
36
37 /**
38 * Abstract {@code CipherService} implementation utilizing Java's JCA APIs.
39 * <h2>Auto-generated Initialization Vectors</h2>
40 * Shiro does something by default for all of its {@code CipherService} implementations that the JCA
41 * {@link javax.crypto.Cipher Cipher} does not do: by default,
42 * <a href="http://en.wikipedia.org/wiki/Initialization_vector">initialization vector</a>s are automatically randomly
43 * generated and prepended to encrypted data before returning from the {@code encrypt} methods. That is, the returned
44 * byte array or {@code OutputStream} is actually a concatenation of an initialization vector byte array plus the actual
45 * encrypted data byte array. The {@code decrypt} methods in turn know to read this prepended initialization vector
46 * before decrypting the real data that follows.
47 * <p/>
48 * This is highly desirable because initialization vectors guarantee that, for a key and any plaintext, the encrypted
49 * output will always be different <em>even if you call {@code encrypt} multiple times with the exact same arguments</em>.
50 * This is essential in cryptography to ensure that data patterns cannot be identified across multiple input sources
51 * that are the same or similar.
52 * <p/>
53 * You can turn off this behavior by setting the
54 * {@link #setGenerateInitializationVectors(boolean) generateInitializationVectors} property to {@code false}, but it
55 * is highly recommended that you do not do this unless you have a very good reason to do so, since you would be losing
56 * a critical security feature.
57 * <h3>Initialization Vector Size</h3>
58 * This implementation defaults the {@link #setInitializationVectorSize(int) initializationVectorSize} attribute to
59 * {@code 128} bits, a fairly common size. Initialization vector sizes are very algorithm specific however, so subclass
60 * implementations will often override this value in their constructor if necessary.
61 * <p/>
62 * Also note that {@code initializationVectorSize} values are specified in the number of
63 * bits (not bytes!) to match common references in most cryptography documentation. In practice though, initialization
64 * vectors are always specified as a byte array, so ensure that if you set this property, that the value is a multiple
65 * of {@code 8} to ensure that the IV can be correctly represented as a byte array (the
66 * {@link #setInitializationVectorSize(int) setInitializationVectorSize} mutator method enforces this).
67 *
68 * @since 1.0
69 */
70 @SuppressWarnings("checkstyle:MethodCount")
71 public abstract class JcaCipherService implements CipherService {
72
73 /**
74 * Internal private log instance.
75 */
76 private static final Logger LOGGER = LoggerFactory.getLogger(JcaCipherService.class);
77
78 /**
79 * Default key size (in bits) for generated keys.
80 */
81 private static final int DEFAULT_KEY_SIZE = 128;
82
83 /**
84 * Default size of the internal buffer (in bytes) used to transfer data between streams during stream operations
85 */
86 private static final int DEFAULT_STREAMING_BUFFER_SIZE = 512;
87
88 private static final int BITS_PER_BYTE = 8;
89
90 /**
91 * Default SecureRandom algorithm name to use when acquiring the SecureRandom instance.
92 */
93 private static final String RANDOM_NUM_GENERATOR_ALGORITHM_NAME = "SHA1PRNG";
94
95 /**
96 * The name of the cipher algorithm to use for all encryption, decryption, and key operations
97 */
98 private String algorithmName;
99
100 /**
101 * The size in bits (not bytes) of generated cipher keys
102 */
103 private int keySize;
104
105 /**
106 * The size of the internal buffer (in bytes) used to transfer data from one stream to another during stream operations
107 */
108 private int streamingBufferSize;
109
110 private boolean generateInitializationVectors;
111 private int initializationVectorSize;
112
113
114 private SecureRandom secureRandom;
115
116 /**
117 * Creates a new {@code JcaCipherService} instance which will use the specified cipher {@code algorithmName}
118 * for all encryption, decryption, and key operations. Also, the following defaults are set:
119 * <ul>
120 * <li>{@link #setKeySize keySize} = 128 bits</li>
121 * <li>{@link #setInitializationVectorSize(int) initializationVectorSize} = 128 bits</li>
122 * <li>{@link #setStreamingBufferSize(int) streamingBufferSize} = 512 bytes</li>
123 * </ul>
124 *
125 * @param algorithmName the name of the cipher algorithm to use for all encryption, decryption, and key operations
126 */
127 protected JcaCipherService(String algorithmName) {
128 if (!StringUtils.hasText(algorithmName)) {
129 throw new IllegalArgumentException("algorithmName argument cannot be null or empty.");
130 }
131 this.algorithmName = algorithmName;
132 this.keySize = DEFAULT_KEY_SIZE;
133 //default to same size as the key size (a common algorithm practice)
134 this.initializationVectorSize = DEFAULT_KEY_SIZE;
135 this.streamingBufferSize = DEFAULT_STREAMING_BUFFER_SIZE;
136 this.generateInitializationVectors = true;
137 }
138
139 /**
140 * Returns the cipher algorithm name that will be used for all encryption, decryption, and key operations (for
141 * example, 'AES', 'Blowfish', 'RSA', 'DSA', 'TripleDES', etc.).
142 *
143 * @return the cipher algorithm name that will be used for all encryption, decryption, and key operations
144 */
145 public String getAlgorithmName() {
146 return algorithmName;
147 }
148
149 /**
150 * Returns the size in bits (not bytes) of generated cipher keys.
151 *
152 * @return the size in bits (not bytes) of generated cipher keys.
153 */
154 public int getKeySize() {
155 return keySize;
156 }
157
158 /**
159 * Sets the size in bits (not bytes) of generated cipher keys.
160 *
161 * @param keySize the size in bits (not bytes) of generated cipher keys.
162 */
163 public void setKeySize(int keySize) {
164 this.keySize = keySize;
165 }
166
167 public boolean isGenerateInitializationVectors() {
168 return generateInitializationVectors;
169 }
170
171 public void setGenerateInitializationVectors(boolean generateInitializationVectors) {
172 this.generateInitializationVectors = generateInitializationVectors;
173 }
174
175 /**
176 * Returns the algorithm-specific size in bits of generated initialization vectors.
177 *
178 * @return the algorithm-specific size in bits of generated initialization vectors.
179 */
180 public int getInitializationVectorSize() {
181 return initializationVectorSize;
182 }
183
184 /**
185 * Sets the algorithm-specific initialization vector size in bits (not bytes!) to be used when generating
186 * initialization vectors. The value must be a multiple of {@code 8} to ensure that the IV can be represented
187 * as a byte array.
188 *
189 * @param initializationVectorSize the size in bits (not bytes) of generated initialization vectors.
190 * @throws IllegalArgumentException if the size is not a multiple of {@code 8}.
191 */
192 public void setInitializationVectorSize(int initializationVectorSize) throws IllegalArgumentException {
193 if (initializationVectorSize % BITS_PER_BYTE != 0) {
194 String msg = "Initialization vector sizes are specified in bits, but must be a multiple of 8 so they "
195 + "can be easily represented as a byte array.";
196 throw new IllegalArgumentException(msg);
197 }
198 this.initializationVectorSize = initializationVectorSize;
199 }
200
201 protected boolean isGenerateInitializationVectors(boolean streaming) {
202 return isGenerateInitializationVectors();
203 }
204
205 /**
206 * Returns the size in bytes of the internal buffer used to transfer data from one stream to another during stream
207 * operations ({@link #encrypt(java.io.InputStream, java.io.OutputStream, byte[])} and
208 * {@link #decrypt(java.io.InputStream, java.io.OutputStream, byte[])}).
209 * <p/>
210 * Default size is {@code 512} bytes.
211 *
212 * @return the size of the internal buffer used to transfer data from one stream to another during stream
213 * operations
214 */
215 public int getStreamingBufferSize() {
216 return streamingBufferSize;
217 }
218
219 /**
220 * Sets the size in bytes of the internal buffer used to transfer data from one stream to another during stream
221 * operations ({@link #encrypt(java.io.InputStream, java.io.OutputStream, byte[])} and
222 * {@link #decrypt(java.io.InputStream, java.io.OutputStream, byte[])}).
223 * <p/>
224 * Default size is {@code 512} bytes.
225 *
226 * @param streamingBufferSize the size of the internal buffer used to transfer data from one stream to another
227 * during stream operations
228 */
229 public void setStreamingBufferSize(int streamingBufferSize) {
230 this.streamingBufferSize = streamingBufferSize;
231 }
232
233 /**
234 * Returns a source of randomness for encryption operations. If one is not configured, and the underlying
235 * algorithm needs one, the JDK {@code SHA1PRNG} instance will be used by default.
236 *
237 * @return a source of randomness for encryption operations. If one is not configured, and the underlying
238 * algorithm needs one, the JDK {@code SHA1PRNG} instance will be used by default.
239 */
240 public SecureRandom getSecureRandom() {
241 return secureRandom;
242 }
243
244 /**
245 * Sets a source of randomness for encryption operations. If one is not configured, and the underlying
246 * algorithm needs one, the JDK {@code SHA1PRNG} instance will be used by default.
247 *
248 * @param secureRandom a source of randomness for encryption operations. If one is not configured, and the
249 * underlying algorithm needs one, the JDK {@code SHA1PRNG} instance will be used by default.
250 */
251 public void setSecureRandom(SecureRandom secureRandom) {
252 this.secureRandom = secureRandom;
253 }
254
255 protected static SecureRandom getDefaultSecureRandom() {
256 try {
257 return java.security.SecureRandom.getInstance(RANDOM_NUM_GENERATOR_ALGORITHM_NAME);
258 } catch (java.security.NoSuchAlgorithmException e) {
259 LOGGER.debug("The SecureRandom SHA1PRNG algorithm is not available on the current platform. Using the "
260 + "platform's default SecureRandom algorithm.", e);
261 return new java.security.SecureRandom();
262 }
263 }
264
265 protected SecureRandom ensureSecureRandom() {
266 SecureRandom random = getSecureRandom();
267 if (random == null) {
268 random = getDefaultSecureRandom();
269 }
270 return random;
271 }
272
273 /**
274 * Returns the transformation string to use with the {@link javax.crypto.Cipher#getInstance} invocation when
275 * creating a new {@code Cipher} instance. This default implementation always returns
276 * {@link #getAlgorithmName() getAlgorithmName()}. Block cipher implementations will want to override this method
277 * to support appending cipher operation modes and padding schemes.
278 *
279 * @param streaming if the transformation string is going to be used for a Cipher for stream-based encryption or not.
280 * @return the transformation string to use with the {@link javax.crypto.Cipher#getInstance} invocation when
281 * creating a new {@code Cipher} instance.
282 */
283 protected String getTransformationString(boolean streaming) {
284 return getAlgorithmName();
285 }
286
287 protected byte[] generateInitializationVector(boolean streaming) {
288 int size = getInitializationVectorSize();
289 if (size <= 0) {
290 String msg = "initializationVectorSize property must be greater than zero. This number is "
291 + "typically set in the " + CipherService.class.getSimpleName() + " subclass constructor. "
292 + "Also check your configuration to ensure that if you are setting a value, it is positive.";
293 throw new IllegalStateException(msg);
294 }
295 if (size % BITS_PER_BYTE != 0) {
296 String msg = "initializationVectorSize property must be a multiple of 8 to represent as a byte array.";
297 throw new IllegalStateException(msg);
298 }
299 int sizeInBytes = size / BITS_PER_BYTE;
300 byte[] ivBytes = new byte[sizeInBytes];
301 SecureRandom random = ensureSecureRandom();
302 random.nextBytes(ivBytes);
303 return ivBytes;
304 }
305
306 public ByteSource encrypt(byte[] plaintext, byte[] key) {
307 byte[] ivBytes = null;
308 boolean generate = isGenerateInitializationVectors(false);
309 if (generate) {
310 ivBytes = generateInitializationVector(false);
311 if (ivBytes == null || ivBytes.length == 0) {
312 throw new IllegalStateException("Initialization vector generation is enabled - generated vector "
313 + "cannot be null or empty.");
314 }
315 }
316 return encrypt(plaintext, key, ivBytes, generate);
317 }
318
319 private ByteSource encrypt(byte[] plaintext, byte[] key, byte[] iv, boolean prependIv) throws CryptoException {
320
321 final int mode = javax.crypto.Cipher.ENCRYPT_MODE;
322
323 byte[] output;
324
325 if (prependIv && iv != null && iv.length > 0) {
326
327 byte[] encrypted = crypt(plaintext, key, iv, mode);
328
329 output = new byte[iv.length + encrypted.length];
330
331 //now copy the iv bytes + encrypted bytes into one output array:
332
333 // iv bytes:
334 System.arraycopy(iv, 0, output, 0, iv.length);
335
336 // + encrypted bytes:
337 System.arraycopy(encrypted, 0, output, iv.length, encrypted.length);
338 } else {
339 output = crypt(plaintext, key, iv, mode);
340 }
341
342 if (LOGGER.isTraceEnabled()) {
343 LOGGER.trace("Incoming plaintext of size " + (plaintext != null ? plaintext.length : 0) + ". Ciphertext "
344 + "byte array is size " + (output != null ? output.length : 0));
345 }
346
347 return ByteSource.Util.bytes(output);
348 }
349
350 public ByteSourceBroker decrypt(byte[] ciphertext, byte[] key) throws CryptoException {
351 return new SimpleByteSourceBroker(this, ciphertext, key);
352 }
353
354 ByteSource decryptInternal(byte[] ciphertext, byte[] key) throws CryptoException {
355
356 byte[] encrypted = ciphertext;
357
358 //No IV, check if we need to read the IV from the stream:
359 byte[] iv = null;
360
361 if (isGenerateInitializationVectors(false)) {
362 try {
363 //We are generating IVs, so the ciphertext argument array is not actually 100% cipher text. Instead, it
364 //is:
365 // - the first N bytes is the initialization vector, where N equals the value of the
366 // 'initializationVectorSize' attribute.
367 // - the remaining bytes in the method argument (arg.length - N) is the real cipher text.
368
369 //So we need to chunk the method argument into its constituent parts to find the IV and then use
370 //the IV to decrypt the real ciphertext:
371
372 int ivSize = getInitializationVectorSize();
373 int ivByteSize = ivSize / BITS_PER_BYTE;
374
375 //now we know how large the iv is, so extract the iv bytes:
376 iv = new byte[ivByteSize];
377 System.arraycopy(ciphertext, 0, iv, 0, ivByteSize);
378
379 //remaining data is the actual encrypted ciphertext. Isolate it:
380 int encryptedSize = ciphertext.length - ivByteSize;
381 encrypted = new byte[encryptedSize];
382 System.arraycopy(ciphertext, ivByteSize, encrypted, 0, encryptedSize);
383 } catch (Exception e) {
384 String msg = "Unable to correctly extract the Initialization Vector or ciphertext.";
385 throw new CryptoException(msg, e);
386 }
387 }
388
389 return decryptInternal(encrypted, key, iv);
390 }
391
392 private ByteSource decryptInternal(byte[] ciphertext, byte[] key, byte[] iv) throws CryptoException {
393 if (LOGGER.isTraceEnabled()) {
394 LOGGER.trace("Attempting to decrypt incoming byte array of length "
395 + (ciphertext != null ? ciphertext.length : 0));
396 }
397 byte[] decrypted = crypt(ciphertext, key, iv, javax.crypto.Cipher.DECRYPT_MODE);
398 return decrypted == null ? null : ByteSource.Util.bytes(decrypted);
399 }
400
401 /**
402 * Returns a new {@link javax.crypto.Cipher Cipher} instance to use for encryption/decryption operations. The
403 * Cipher's {@code transformationString} for the {@code Cipher}.{@link javax.crypto.Cipher#getInstance getInstance}
404 * call is obtained via the {@link #getTransformationString(boolean) getTransformationString} method.
405 *
406 * @param streaming {@code true} if the cipher instance will be used as a stream cipher, {@code false} if it will be
407 * used as a block cipher.
408 * @return a new JDK {@code Cipher} instance.
409 * @throws CryptoException if a new Cipher instance cannot be constructed based on the
410 * {@link #getTransformationString(boolean) getTransformationString} value.
411 */
412 private javax.crypto.Cipher newCipherInstance(boolean streaming) throws CryptoException {
413 String transformationString = getTransformationString(streaming);
414 try {
415 return javax.crypto.Cipher.getInstance(transformationString);
416 } catch (Exception e) {
417 String msg = "Unable to acquire a Java JCA Cipher instance using "
418 + javax.crypto.Cipher.class.getName() + ".getInstance( \"" + transformationString + "\" ). "
419 + getAlgorithmName() + " under this configuration is required for the "
420 + getClass().getName() + " instance to function.";
421 throw new CryptoException(msg, e);
422 }
423 }
424
425 /**
426 * Functions as follows:
427 * <ol>
428 * <li>Creates a {@link #newCipherInstance(boolean) new JDK cipher instance}</li>
429 * <li>Converts the specified key bytes into an {@link #getAlgorithmName() algorithm}-compatible JDK
430 * {@link Key key} instance</li>
431 * <li>{@link #init(javax.crypto.Cipher, int, java.security.Key, AlgorithmParameterSpec, SecureRandom) Initializes}
432 * the JDK cipher instance with the JDK key</li>
433 * <li>Calls the {@link #crypt(javax.crypto.Cipher, byte[]) crypt(cipher,bytes)} method to either encrypt or
434 * decrypt the data based on the specified Cipher behavior mode
435 * ({@link javax.crypto.Cipher#ENCRYPT_MODE Cipher.ENCRYPT_MODE} or
436 * {@link javax.crypto.Cipher#DECRYPT_MODE Cipher.DECRYPT_MODE})</li>
437 * </ol>
438 *
439 * @param bytes the bytes to crypt
440 * @param key the key to use to perform the encryption or decryption.
441 * @param iv the initialization vector to use for the crypt operation (optional, may be {@code null}).
442 * @param mode the JDK Cipher behavior mode (Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE).
443 * @return the resulting crypted byte array
444 * @throws IllegalArgumentException if {@code bytes} are null or empty.
445 * @throws CryptoException if Cipher initialization or the crypt operation fails
446 */
447 private byte[] crypt(byte[] bytes, byte[] key, byte[] iv, int mode) throws IllegalArgumentException, CryptoException {
448 if (key == null || key.length == 0) {
449 throw new IllegalArgumentException("key argument cannot be null or empty.");
450 }
451 javax.crypto.Cipher cipher = initNewCipher(mode, key, iv, false);
452 return crypt(cipher, bytes);
453 }
454
455 /**
456 * Calls the {@link javax.crypto.Cipher#doFinal(byte[]) doFinal(bytes)} method, propagating any exception that
457 * might arise in an {@link CryptoException}
458 *
459 * @param cipher the JDK Cipher to finalize (perform the actual encryption)
460 * @param bytes the bytes to crypt
461 * @return the resulting crypted byte array.
462 * @throws CryptoException if there is an illegal block size or bad padding
463 */
464 private byte[] crypt(javax.crypto.Cipher cipher, byte[] bytes) throws CryptoException {
465 try {
466 return cipher.doFinal(bytes);
467 } catch (Exception e) {
468 String msg = "Unable to execute 'doFinal' with cipher instance [" + cipher + "].";
469 throw new CryptoException(msg, e);
470 }
471 }
472
473 /**
474 * Initializes the JDK Cipher with the specified mode and key. This is primarily a utility method to catch any
475 * potential {@link java.security.InvalidKeyException InvalidKeyException} that might arise.
476 *
477 * @param cipher the JDK Cipher to {@link javax.crypto.Cipher#init(int, java.security.Key) init}.
478 * @param mode the Cipher mode
479 * @param key the Cipher's Key
480 * @param spec the JDK AlgorithmParameterSpec for cipher initialization (optional, may be null).
481 * @param random the SecureRandom to use for cipher initialization (optional, may be null).
482 * @throws CryptoException if the key is invalid
483 */
484 private void init(javax.crypto.Cipher cipher, int mode, java.security.Key key,
485 AlgorithmParameterSpec spec, SecureRandom random) throws CryptoException {
486 try {
487 if (random != null) {
488 if (spec != null) {
489 cipher.init(mode, key, spec, random);
490 } else {
491 cipher.init(mode, key, random);
492 }
493 } else {
494 if (spec != null) {
495 cipher.init(mode, key, spec);
496 } else {
497 cipher.init(mode, key);
498 }
499 }
500 } catch (Exception e) {
501 String msg = "Unable to init cipher instance.";
502 throw new CryptoException(msg, e);
503 }
504 }
505
506
507 public void encrypt(InputStream in, OutputStream out, byte[] key) throws CryptoException {
508 byte[] iv = null;
509 boolean generate = isGenerateInitializationVectors(true);
510 if (generate) {
511 iv = generateInitializationVector(true);
512 if (iv == null || iv.length == 0) {
513 throw new IllegalStateException("Initialization vector generation is enabled - generated vector "
514 + "cannot be null or empty.");
515 }
516 }
517 encrypt(in, out, key, iv, generate);
518 }
519
520 private void encrypt(InputStream in, OutputStream out, byte[] key, byte[] iv, boolean prependIv) throws CryptoException {
521 if (prependIv && iv != null && iv.length > 0) {
522 try {
523 //first write the IV:
524 out.write(iv);
525 } catch (IOException e) {
526 throw new CryptoException(e);
527 }
528 }
529
530 crypt(in, out, key, iv, javax.crypto.Cipher.ENCRYPT_MODE);
531 }
532
533 public void decrypt(InputStream in, OutputStream out, byte[] key) throws CryptoException {
534 decrypt(in, out, key, isGenerateInitializationVectors(true));
535 }
536
537 private void decrypt(InputStream in, OutputStream out, byte[] key, boolean ivPrepended) throws CryptoException {
538
539 byte[] iv = null;
540 //No Initialization Vector provided as a method argument - check if we need to read the IV from the stream:
541 if (ivPrepended) {
542 //we are generating IVs, so we need to read the previously-generated IV from the stream before
543 //we decrypt the rest of the stream (we need the IV to decrypt):
544 int ivSize = getInitializationVectorSize();
545 int ivByteSize = ivSize / BITS_PER_BYTE;
546 iv = new byte[ivByteSize];
547 int read;
548
549 try {
550 read = in.read(iv);
551 } catch (IOException e) {
552 String msg = "Unable to correctly read the Initialization Vector from the input stream.";
553 throw new CryptoException(msg, e);
554 }
555
556 if (read != ivByteSize) {
557 throw new CryptoException("Unable to read initialization vector bytes from the InputStream. "
558 + "This is required when initialization vectors are autogenerated during an encryption operation.");
559 }
560 }
561
562 decrypt(in, out, key, iv);
563 }
564
565 private void decrypt(InputStream in, OutputStream out, byte[] decryptionKey, byte[] iv) throws CryptoException {
566 crypt(in, out, decryptionKey, iv, javax.crypto.Cipher.DECRYPT_MODE);
567 }
568
569 private void crypt(InputStream in, OutputStream out, byte[] keyBytes, byte[] iv, int cryptMode) throws CryptoException {
570 if (in == null) {
571 throw new NullPointerException("InputStream argument cannot be null.");
572 }
573 if (out == null) {
574 throw new NullPointerException("OutputStream argument cannot be null.");
575 }
576
577 javax.crypto.Cipher cipher = initNewCipher(cryptMode, keyBytes, iv, true);
578
579 CipherInputStream cis = new CipherInputStream(in, cipher);
580
581 int bufSize = getStreamingBufferSize();
582 byte[] buffer = new byte[bufSize];
583
584 int bytesRead;
585 try {
586 while ((bytesRead = cis.read(buffer)) != -1) {
587 out.write(buffer, 0, bytesRead);
588 }
589 } catch (IOException e) {
590 throw new CryptoException(e);
591 }
592 }
593
594 private javax.crypto.Cipher initNewCipher(int jcaCipherMode, byte[] key, byte[] iv, boolean streaming)
595 throws CryptoException {
596
597 javax.crypto.Cipher cipher = newCipherInstance(streaming);
598 java.security.Key jdkKey = new SecretKeySpec(key, getAlgorithmName());
599 AlgorithmParameterSpec ivSpec = null;
600
601 if (iv != null && iv.length > 0) {
602 ivSpec = createParameterSpec(iv, streaming);
603 }
604
605 init(cipher, jcaCipherMode, jdkKey, ivSpec, getSecureRandom());
606
607 return cipher;
608 }
609
610 protected AlgorithmParameterSpec createParameterSpec(byte[] iv, boolean streaming) {
611 return new IvParameterSpec(iv);
612 }
613 }