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