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, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.commons.crypto.stream; 019 020import java.io.IOException; 021import java.io.InputStream; 022import java.lang.reflect.Method; 023import java.nio.ByteBuffer; 024import java.nio.channels.ReadableByteChannel; 025import java.security.GeneralSecurityException; 026import java.security.Key; 027import java.security.spec.AlgorithmParameterSpec; 028import java.util.Objects; 029import java.util.Properties; 030 031import javax.crypto.BadPaddingException; 032import javax.crypto.Cipher; 033import javax.crypto.IllegalBlockSizeException; 034import javax.crypto.ShortBufferException; 035import javax.crypto.spec.IvParameterSpec; 036 037import org.apache.commons.crypto.Crypto; 038import org.apache.commons.crypto.cipher.CryptoCipher; 039import org.apache.commons.crypto.stream.input.ChannelInput; 040import org.apache.commons.crypto.stream.input.Input; 041import org.apache.commons.crypto.stream.input.StreamInput; 042import org.apache.commons.crypto.utils.Utils; 043 044/** 045 * CryptoInputStream reads input data and decrypts data in stream manner. It 046 * supports any mode of operations such as AES CBC/CTR/GCM mode in concept.It is 047 * not thread-safe. 048 * 049 */ 050 051public class CryptoInputStream extends InputStream implements 052 ReadableByteChannel { 053 private final byte[] oneByteBuf = new byte[1]; 054 055 /** 056 * The configuration key of the buffer size for stream. 057 */ 058 public static final String STREAM_BUFFER_SIZE_KEY = Crypto.CONF_PREFIX 059 + "stream.buffer.size"; 060 061 /** The CryptoCipher instance. */ 062 final CryptoCipher cipher; // package protected for access by crypto classes; do not expose further 063 064 /** The buffer size. */ 065 private final int bufferSize; 066 067 /** Crypto key for the cipher. */ 068 final Key key; // package protected for access by crypto classes; do not expose further 069 070 /** the algorithm parameters */ 071 private final AlgorithmParameterSpec params; 072 073 /** Flag to mark whether the input stream is closed. */ 074 private boolean closed; 075 076 /** 077 * Flag to mark whether do final of the cipher to end the decrypting stream. 078 */ 079 private boolean finalDone = false; 080 081 /** The input data. */ 082 Input input; // package protected for access by crypto classes; do not expose further 083 084 /** 085 * Input data buffer. The data starts at inBuffer.position() and ends at to 086 * inBuffer.limit(). 087 */ 088 ByteBuffer inBuffer; // package protected for access by crypto classes; do not expose further 089 090 /** 091 * The decrypted data buffer. The data starts at outBuffer.position() and 092 * ends at outBuffer.limit(). 093 */ 094 ByteBuffer outBuffer; // package protected for access by crypto classes; do not expose further 095 096 // stream related configuration keys 097 /** 098 * The default value of the buffer size for stream. 099 */ 100 private static final int STREAM_BUFFER_SIZE_DEFAULT = 8192; 101 102 private static final int MIN_BUFFER_SIZE = 512; 103 104 /** 105 * Constructs a {@link CryptoInputStream}. 106 * 107 * @param transformation the name of the transformation, e.g., 108 * <i>AES/CBC/PKCS5Padding</i>. 109 * See the Java Cryptography Architecture Standard Algorithm Name Documentation 110 * for information about standard transformation names. 111 * @param properties The {@code Properties} class represents a set of 112 * properties. 113 * @param inputStream the input stream. 114 * @param key crypto key for the cipher. 115 * @param params the algorithm parameters. 116 * @throws IOException if an I/O error occurs. 117 */ 118 @SuppressWarnings("resource") // The CryptoCipher returned by getCipherInstance() is closed by CryptoInputStream. 119 public CryptoInputStream(final String transformation, 120 final Properties properties, final InputStream inputStream, final Key key, 121 final AlgorithmParameterSpec params) throws IOException { 122 this(inputStream, Utils.getCipherInstance(transformation, properties), 123 CryptoInputStream.getBufferSize(properties), key, params); 124 } 125 126 /** 127 * Constructs a {@link CryptoInputStream}. 128 * 129 * @param transformation the name of the transformation, e.g., 130 * <i>AES/CBC/PKCS5Padding</i>. 131 * See the Java Cryptography Architecture Standard Algorithm Name Documentation 132 * for information about standard transformation names. 133 * @param properties The {@code Properties} class represents a set of 134 * properties. 135 * @param channel the ReadableByteChannel object. 136 * @param key crypto key for the cipher. 137 * @param params the algorithm parameters. 138 * @throws IOException if an I/O error occurs. 139 */ 140 @SuppressWarnings("resource") // The CryptoCipher returned by getCipherInstance() is closed by CryptoInputStream. 141 public CryptoInputStream(final String transformation, 142 final Properties properties, final ReadableByteChannel channel, final Key key, 143 final AlgorithmParameterSpec params) throws IOException { 144 this(channel, Utils.getCipherInstance(transformation, properties), CryptoInputStream 145 .getBufferSize(properties), key, params); 146 } 147 148 /** 149 * Constructs a {@link CryptoInputStream}. 150 * 151 * @param cipher the cipher instance. 152 * @param inputStream the input stream. 153 * @param bufferSize the bufferSize. 154 * @param key crypto key for the cipher. 155 * @param params the algorithm parameters. 156 * @throws IOException if an I/O error occurs. 157 */ 158 protected CryptoInputStream(final InputStream inputStream, final CryptoCipher cipher, 159 final int bufferSize, final Key key, final AlgorithmParameterSpec params) 160 throws IOException { 161 this(new StreamInput(inputStream, bufferSize), cipher, bufferSize, key, params); 162 } 163 164 /** 165 * Constructs a {@link CryptoInputStream}. 166 * 167 * @param channel the ReadableByteChannel instance. 168 * @param cipher the cipher instance. 169 * @param bufferSize the bufferSize. 170 * @param key crypto key for the cipher. 171 * @param params the algorithm parameters. 172 * @throws IOException if an I/O error occurs. 173 */ 174 protected CryptoInputStream(final ReadableByteChannel channel, final CryptoCipher cipher, 175 final int bufferSize, final Key key, final AlgorithmParameterSpec params) 176 throws IOException { 177 this(new ChannelInput(channel), cipher, bufferSize, key, params); 178 } 179 180 /** 181 * Constructs a {@link CryptoInputStream}. 182 * 183 * @param input the input data. 184 * @param cipher the cipher instance. 185 * @param bufferSize the bufferSize. 186 * @param key crypto key for the cipher. 187 * @param params the algorithm parameters. 188 * @throws IOException if an I/O error occurs. 189 */ 190 protected CryptoInputStream(final Input input, final CryptoCipher cipher, final int bufferSize, 191 final Key key, final AlgorithmParameterSpec params) throws IOException { 192 this.input = input; 193 this.cipher = cipher; 194 this.bufferSize = CryptoInputStream.checkBufferSize(cipher, bufferSize); 195 196 this.key = key; 197 this.params = params; 198 if (!(params instanceof IvParameterSpec)) { 199 // other AlgorithmParameterSpec such as GCMParameterSpec is not 200 // supported now. 201 throw new IOException("Illegal parameters"); 202 } 203 204 inBuffer = ByteBuffer.allocateDirect(this.bufferSize); 205 outBuffer = ByteBuffer.allocateDirect(this.bufferSize 206 + cipher.getBlockSize()); 207 outBuffer.limit(0); 208 209 initCipher(); 210 } 211 212 /** 213 * Overrides the {@link java.io.InputStream#read()}. Reads the next byte of 214 * data from the input stream. 215 * 216 * @return the next byte of data, or {@code -1} if the end of the 217 * stream is reached. 218 * @throws IOException if an I/O error occurs. 219 */ 220 @Override 221 public int read() throws IOException { 222 int n; 223 while ((n = read(oneByteBuf, 0, 1)) == 0) { //NOPMD 224 /* no op */ 225 } 226 return (n == -1) ? -1 : oneByteBuf[0] & 0xff; 227 } 228 229 /** 230 * Overrides the {@link java.io.InputStream#read(byte[], int, int)}. 231 * Decryption is buffer based. If there is data in {@link #outBuffer}, then 232 * read it out of this buffer. If there is no data in {@link #outBuffer}, 233 * then read more from the underlying stream and do the decryption. 234 * 235 * @param array the buffer into which the decrypted data is read. 236 * @param off the buffer offset. 237 * @param len the maximum number of decrypted data bytes to read. 238 * @return int the total number of decrypted data bytes read into the 239 * buffer. 240 * @throws IOException if an I/O error occurs. 241 */ 242 @Override 243 public int read(final byte[] array, final int off, final int len) throws IOException { 244 checkStream(); 245 Objects.requireNonNull(array, "array"); 246 if (off < 0 || len < 0 || len > array.length - off) { 247 throw new IndexOutOfBoundsException(); 248 } else if (len == 0) { 249 return 0; 250 } 251 252 final int remaining = outBuffer.remaining(); 253 if (remaining > 0) { 254 // Satisfy the read with the existing data 255 final int n = Math.min(len, remaining); 256 outBuffer.get(array, off, n); 257 return n; 258 } 259 // No data in the out buffer, try read new data and decrypt it 260 // we loop for new data 261 int nd = 0; 262 while (nd == 0) { 263 nd = decryptMore(); 264 } 265 if (nd < 0) { 266 return nd; 267 } 268 269 final int n = Math.min(len, outBuffer.remaining()); 270 outBuffer.get(array, off, n); 271 return n; 272 } 273 274 /** 275 * Overrides the {@link java.io.InputStream#skip(long)}. Skips over and 276 * discards {@code n} bytes of data from this input stream. 277 * 278 * @param n the number of bytes to be skipped. 279 * @return the actual number of bytes skipped. 280 * @throws IOException if an I/O error occurs. 281 */ 282 @Override 283 public long skip(final long n) throws IOException { 284 Utils.checkArgument(n >= 0, "Negative skip length."); 285 checkStream(); 286 287 if (n == 0) { 288 return 0; 289 } 290 291 long remaining = n; 292 int nd; 293 294 while (remaining > 0) { 295 if (remaining <= outBuffer.remaining()) { 296 // Skip in the remaining buffer 297 final int pos = outBuffer.position() + (int) remaining; 298 outBuffer.position(pos); 299 300 remaining = 0; 301 break; 302 } 303 remaining -= outBuffer.remaining(); 304 outBuffer.clear(); 305 306 // we loop for new data 307 nd = 0; 308 while (nd == 0) { 309 nd = decryptMore(); 310 } 311 if (nd < 0) { 312 break; 313 } 314 } 315 316 return n - remaining; 317 } 318 319 /** 320 * Overrides the {@link InputStream#available()}. Returns an estimate of the 321 * number of bytes that can be read (or skipped over) from this input stream 322 * without blocking by the next invocation of a method for this input 323 * stream. 324 * 325 * @return an estimate of the number of bytes that can be read (or skipped 326 * over) from this input stream without blocking or {@code 0} when 327 * it reaches the end of the input stream. 328 * @throws IOException if an I/O error occurs. 329 */ 330 @Override 331 public int available() throws IOException { 332 checkStream(); 333 334 return input.available() + outBuffer.remaining(); 335 } 336 337 /** 338 * Overrides the {@link InputStream#close()}. Closes this input stream and 339 * releases any system resources associated with the stream. 340 * 341 * @throws IOException if an I/O error occurs. 342 */ 343 @Override 344 public void close() throws IOException { 345 if (closed) { 346 return; 347 } 348 349 input.close(); 350 freeBuffers(); 351 cipher.close(); 352 super.close(); 353 closed = true; 354 } 355 356 /** 357 * Overrides the {@link InputStream#markSupported()}. 358 * 359 * @return false,the {@link CtrCryptoInputStream} don't support the mark 360 * method. 361 */ 362 @Override 363 public boolean markSupported() { 364 return false; 365 } 366 367 /** 368 * Overrides the {@link java.nio.channels.Channel#isOpen()}. 369 * 370 * @return {@code true} if, and only if, this channel is open. 371 */ 372 @Override 373 public boolean isOpen() { 374 return !closed; 375 } 376 377 /** 378 * Overrides the 379 * {@link java.nio.channels.ReadableByteChannel#read(ByteBuffer)}. Reads a 380 * sequence of bytes from this channel into the given buffer. 381 * 382 * @param dst The buffer into which bytes are to be transferred. 383 * @return The number of bytes read, possibly zero, or {@code -1} if the 384 * channel has reached end-of-stream. 385 * @throws IOException if an I/O error occurs. 386 */ 387 @Override 388 public int read(final ByteBuffer dst) throws IOException { 389 checkStream(); 390 int remaining = outBuffer.remaining(); 391 if (remaining <= 0) { 392 // Decrypt more data 393 // we loop for new data 394 int nd = 0; 395 while (nd == 0) { 396 nd = decryptMore(); 397 } 398 399 if (nd < 0) { 400 return -1; 401 } 402 } 403 404 // Copy decrypted data from outBuffer to dst 405 remaining = outBuffer.remaining(); 406 final int toRead = dst.remaining(); 407 if (toRead <= remaining) { 408 final int limit = outBuffer.limit(); 409 outBuffer.limit(outBuffer.position() + toRead); 410 dst.put(outBuffer); 411 outBuffer.limit(limit); 412 return toRead; 413 } 414 dst.put(outBuffer); 415 return remaining; 416 } 417 418 /** 419 * Gets the buffer size. 420 * 421 * @return the bufferSize. 422 */ 423 protected int getBufferSize() { 424 return bufferSize; 425 } 426 427 /** 428 * Gets the key. 429 * 430 * @return the key. 431 */ 432 protected Key getKey() { 433 return key; 434 } 435 436 /** 437 * Gets the internal CryptoCipher. 438 * 439 * @return the cipher instance. 440 */ 441 protected CryptoCipher getCipher() { 442 return cipher; 443 } 444 445 /** 446 * Gets the specification of cryptographic parameters. 447 * 448 * @return the params. 449 */ 450 protected AlgorithmParameterSpec getParams() { 451 return params; 452 } 453 454 /** 455 * Gets the input. 456 * 457 * @return the input. 458 */ 459 protected Input getInput() { 460 return input; 461 } 462 463 /** 464 * Initializes the cipher. 465 * 466 * @throws IOException if an I/O error occurs. 467 */ 468 protected void initCipher() throws IOException { 469 try { 470 cipher.init(Cipher.DECRYPT_MODE, key, params); 471 } catch (final GeneralSecurityException e) { 472 throw new IOException(e); 473 } 474 } 475 476 /** 477 * Decrypts more data by reading the under layer stream. The decrypted data 478 * will be put in the output buffer. If the end of the under stream reached, 479 * we will do final of the cipher to finish all the decrypting of data. 480 * 481 * @return The number of decrypted data. 482 * return -1 (if end of the decrypted stream) 483 * return 0 (no data now, but could have more later) 484 * @throws IOException if an I/O error occurs. 485 */ 486 protected int decryptMore() throws IOException { 487 if (finalDone) { 488 return -1; 489 } 490 491 final int n = input.read(inBuffer); 492 if (n < 0) { 493 // The stream is end, finalize the cipher stream 494 decryptFinal(); 495 496 // Satisfy the read with the remaining 497 final int remaining = outBuffer.remaining(); 498 if (remaining > 0) { 499 return remaining; 500 } 501 502 // End of the stream 503 return -1; 504 } else if (n == 0) { 505 // No data is read, but the stream is not end yet 506 return 0; 507 } else { 508 decrypt(); 509 return outBuffer.remaining(); 510 } 511 } 512 513 /** 514 * Does the decryption using inBuffer as input and outBuffer as output. Upon 515 * return, inBuffer is cleared; the decrypted data starts at 516 * outBuffer.position() and ends at outBuffer.limit(). 517 * 518 * @throws IOException if an I/O error occurs. 519 */ 520 protected void decrypt() throws IOException { 521 // Prepare the input buffer and clear the out buffer 522 inBuffer.flip(); 523 outBuffer.clear(); 524 525 try { 526 cipher.update(inBuffer, outBuffer); 527 } catch (final ShortBufferException e) { 528 throw new IOException(e); 529 } 530 531 // Clear the input buffer and prepare out buffer 532 inBuffer.clear(); 533 outBuffer.flip(); 534 } 535 536 /** 537 * Does final of the cipher to end the decrypting stream. 538 * 539 * @throws IOException if an I/O error occurs. 540 */ 541 protected void decryptFinal() throws IOException { 542 // Prepare the input buffer and clear the out buffer 543 inBuffer.flip(); 544 outBuffer.clear(); 545 546 try { 547 cipher.doFinal(inBuffer, outBuffer); 548 finalDone = true; 549 } catch (final ShortBufferException e) { 550 throw new IOException(e); 551 } catch (final IllegalBlockSizeException e) { 552 throw new IOException(e); 553 } catch (final BadPaddingException e) { 554 throw new IOException(e); 555 } 556 557 // Clear the input buffer and prepare out buffer 558 inBuffer.clear(); 559 outBuffer.flip(); 560 } 561 562 /** 563 * Checks whether the stream is closed. 564 * 565 * @throws IOException if an I/O error occurs. 566 */ 567 protected void checkStream() throws IOException { 568 if (closed) { 569 throw new IOException("Stream closed"); 570 } 571 } 572 573 /** Forcibly free the direct buffers. */ 574 protected void freeBuffers() { 575 CryptoInputStream.freeDirectBuffer(inBuffer); 576 CryptoInputStream.freeDirectBuffer(outBuffer); 577 } 578 579 /** 580 * Forcibly free the direct buffer. 581 * 582 * @param buffer the bytebuffer to be freed. 583 */ 584 static void freeDirectBuffer(final ByteBuffer buffer) { 585 try { 586 /* Using reflection to implement sun.nio.ch.DirectBuffer.cleaner() 587 .clean(); */ 588 final String SUN_CLASS = "sun.nio.ch.DirectBuffer"; 589 final Class<?>[] interfaces = buffer.getClass().getInterfaces(); 590 591 for (final Class<?> clazz : interfaces) { 592 if (clazz.getName().equals(SUN_CLASS)) { 593 final Object[] NO_PARAM = new Object[0]; 594 /* DirectBuffer#cleaner() */ 595 final Method getCleaner = Class.forName(SUN_CLASS).getMethod("cleaner"); 596 final Object cleaner = getCleaner.invoke(buffer, NO_PARAM); 597 /* Cleaner#clean() */ 598 final Method cleanMethod = Class.forName("sun.misc.Cleaner").getMethod("clean"); 599 cleanMethod.invoke(cleaner, NO_PARAM); 600 return; 601 } 602 } 603 } catch (final ReflectiveOperationException e) { // NOPMD 604 // Ignore the Reflection exception. 605 } 606 } 607 608 /** 609 * Reads crypto buffer size. 610 * 611 * @param props The {@code Properties} class represents a set of 612 * properties. 613 * @return the buffer size. 614 * */ 615 static int getBufferSize(final Properties props) { 616 final String bufferSizeStr = props.getProperty(CryptoInputStream.STREAM_BUFFER_SIZE_KEY); 617 if (bufferSizeStr == null || bufferSizeStr.isEmpty()) { 618 return CryptoInputStream.STREAM_BUFFER_SIZE_DEFAULT; 619 } 620 return Integer.parseInt(bufferSizeStr); 621 } 622 623 /** 624 * Checks whether the cipher is supported streaming. 625 * 626 * @param cipher the {@link CryptoCipher} instance. 627 * @throws IOException if an I/O error occurs. 628 */ 629 static void checkStreamCipher(final CryptoCipher cipher) 630 throws IOException { 631 if (!cipher.getAlgorithm().equals("AES/CTR/NoPadding")) { 632 throw new IOException("AES/CTR/NoPadding is required"); 633 } 634 } 635 636 /** 637 * Checks and floors buffer size. 638 * 639 * @param cipher the {@link CryptoCipher} instance. 640 * @param bufferSize the buffer size. 641 * @return the remaining buffer size. 642 */ 643 static int checkBufferSize(final CryptoCipher cipher, final int bufferSize) { 644 Utils.checkArgument(bufferSize >= CryptoInputStream.MIN_BUFFER_SIZE, 645 "Minimum value of buffer size is " + CryptoInputStream.MIN_BUFFER_SIZE + "."); 646 return bufferSize - bufferSize 647 % cipher.getBlockSize(); 648 } 649}