001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.shiro.crypto.cipher; 020 021import org.apache.shiro.crypto.CryptoException; 022import org.apache.shiro.lang.util.ByteSource; 023import org.apache.shiro.lang.util.StringUtils; 024import org.slf4j.Logger; 025import org.slf4j.LoggerFactory; 026 027import javax.crypto.CipherInputStream; 028import javax.crypto.spec.IvParameterSpec; 029import javax.crypto.spec.SecretKeySpec; 030import java.io.IOException; 031import java.io.InputStream; 032import java.io.OutputStream; 033import java.security.Key; 034import java.security.SecureRandom; 035import java.security.spec.AlgorithmParameterSpec; 036 037/** 038 * Abstract {@code CipherService} implementation utilizing Java's JCA APIs. 039 * <h2>Auto-generated Initialization Vectors</h2> 040 * Shiro does something by default for all of its {@code CipherService} implementations that the JCA 041 * {@link javax.crypto.Cipher Cipher} does not do: by default, 042 * <a href="http://en.wikipedia.org/wiki/Initialization_vector">initialization vector</a>s are automatically randomly 043 * generated and prepended to encrypted data before returning from the {@code encrypt} methods. That is, the returned 044 * byte array or {@code OutputStream} is actually a concatenation of an initialization vector byte array plus the actual 045 * encrypted data byte array. The {@code decrypt} methods in turn know to read this prepended initialization vector 046 * before decrypting the real data that follows. 047 * <p/> 048 * This is highly desirable because initialization vectors guarantee that, for a key and any plaintext, the encrypted 049 * output will always be different <em>even if you call {@code encrypt} multiple times with the exact same arguments</em>. 050 * This is essential in cryptography to ensure that data patterns cannot be identified across multiple input sources 051 * that are the same or similar. 052 * <p/> 053 * You can turn off this behavior by setting the 054 * {@link #setGenerateInitializationVectors(boolean) generateInitializationVectors} property to {@code false}, but it 055 * is highly recommended that you do not do this unless you have a very good reason to do so, since you would be losing 056 * a critical security feature. 057 * <h3>Initialization Vector Size</h3> 058 * This implementation defaults the {@link #setInitializationVectorSize(int) initializationVectorSize} attribute to 059 * {@code 128} bits, a fairly common size. Initialization vector sizes are very algorithm specific however, so subclass 060 * implementations will often override this value in their constructor if necessary. 061 * <p/> 062 * Also note that {@code initializationVectorSize} values are specified in the number of 063 * bits (not bytes!) to match common references in most cryptography documentation. In practice though, initialization 064 * vectors are always specified as a byte array, so ensure that if you set this property, that the value is a multiple 065 * of {@code 8} to ensure that the IV can be correctly represented as a byte array (the 066 * {@link #setInitializationVectorSize(int) setInitializationVectorSize} mutator method enforces this). 067 * 068 * @since 1.0 069 */ 070@SuppressWarnings("checkstyle:MethodCount") 071public abstract class JcaCipherService implements CipherService { 072 073 /** 074 * Internal private log instance. 075 */ 076 private static final Logger LOGGER = LoggerFactory.getLogger(JcaCipherService.class); 077 078 /** 079 * Default key size (in bits) for generated keys. 080 */ 081 private static final int DEFAULT_KEY_SIZE = 128; 082 083 /** 084 * Default size of the internal buffer (in bytes) used to transfer data between streams during stream operations 085 */ 086 private static final int DEFAULT_STREAMING_BUFFER_SIZE = 512; 087 088 private static final int BITS_PER_BYTE = 8; 089 090 /** 091 * Default SecureRandom algorithm name to use when acquiring the SecureRandom instance. 092 */ 093 private static final String RANDOM_NUM_GENERATOR_ALGORITHM_NAME = "SHA1PRNG"; 094 095 /** 096 * The name of the cipher algorithm to use for all encryption, decryption, and key operations 097 */ 098 private String algorithmName; 099 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}