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 }