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