PageRenderTime 68ms CodeModel.GetById 16ms RepoModel.GetById 3ms app.codeStats 0ms

/trunk/shine/src/main/java/org/apache/shiro/crypto/JcaCipherService.java

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