CryptoInputStream.java

  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, software
  13.  * distributed under the License is distributed on an "AS IS" BASIS,
  14.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15.  * See the License for the specific language governing permissions and
  16.  * limitations under the License.
  17.  */
  18. package org.apache.commons.crypto.stream;

  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.lang.reflect.Method;
  22. import java.nio.ByteBuffer;
  23. import java.nio.channels.ReadableByteChannel;
  24. import java.security.GeneralSecurityException;
  25. import java.security.Key;
  26. import java.security.spec.AlgorithmParameterSpec;
  27. import java.util.Objects;
  28. import java.util.Properties;

  29. import javax.crypto.BadPaddingException;
  30. import javax.crypto.Cipher;
  31. import javax.crypto.IllegalBlockSizeException;
  32. import javax.crypto.ShortBufferException;
  33. import javax.crypto.spec.IvParameterSpec;

  34. import org.apache.commons.crypto.Crypto;
  35. import org.apache.commons.crypto.cipher.CryptoCipher;
  36. import org.apache.commons.crypto.stream.input.ChannelInput;
  37. import org.apache.commons.crypto.stream.input.Input;
  38. import org.apache.commons.crypto.stream.input.StreamInput;
  39. import org.apache.commons.crypto.utils.AES;
  40. import org.apache.commons.crypto.utils.Utils;

  41. /**
  42.  * CryptoInputStream reads input data and decrypts data in stream manner. It
  43.  * supports any mode of operations such as AES CBC/CTR/GCM mode in concept.It is
  44.  * not thread-safe.
  45.  *
  46.  */

  47. public class CryptoInputStream extends InputStream implements ReadableByteChannel {

  48.     /**
  49.      * The configuration key of the buffer size for stream.
  50.      */
  51.     public static final String STREAM_BUFFER_SIZE_KEY = Crypto.CONF_PREFIX
  52.             + "stream.buffer.size";

  53.     // stream related configuration keys
  54.     /**
  55.      * The default value of the buffer size for stream.
  56.      */
  57.     private static final int STREAM_BUFFER_SIZE_DEFAULT = 8192;

  58.     private static final int MIN_BUFFER_SIZE = 512;

  59.     /**
  60.      * The index value when the end of the stream has been reached {@code -1}.
  61.      *
  62.      * @since 1.1
  63.      */
  64.     public static final int EOS = -1;

  65.     /**
  66.      * Checks and floors buffer size.
  67.      *
  68.      * @param cipher the {@link CryptoCipher} instance.
  69.      * @param bufferSize the buffer size.
  70.      * @return the remaining buffer size.
  71.      */
  72.     static int checkBufferSize(final CryptoCipher cipher, final int bufferSize) {
  73.         Utils.checkArgument(bufferSize >= CryptoInputStream.MIN_BUFFER_SIZE,
  74.                 "Minimum value of buffer size is " + CryptoInputStream.MIN_BUFFER_SIZE + ".");
  75.         return bufferSize - bufferSize % cipher.getBlockSize();
  76.     }

  77.     /**
  78.      * Checks whether the cipher is supported streaming.
  79.      *
  80.      * @param cipher the {@link CryptoCipher} instance.
  81.      * @throws IOException if an I/O error occurs.
  82.      */
  83.     static void checkStreamCipher(final CryptoCipher cipher) throws IOException {
  84.         if (!cipher.getAlgorithm().equals(AES.CTR_NO_PADDING)) {
  85.             throw new IOException(AES.CTR_NO_PADDING + " is required");
  86.         }
  87.     }

  88.     /**
  89.      * Forcibly free the direct buffer.
  90.      *
  91.      * @param buffer the bytebuffer to be freed.
  92.      */
  93.     static void freeDirectBuffer(final ByteBuffer buffer) {
  94.         if (buffer != null) {
  95.             try {
  96.                 /*
  97.                  * Using reflection to implement sun.nio.ch.DirectBuffer.cleaner() .clean();
  98.                  */
  99.                 final String SUN_CLASS = "sun.nio.ch.DirectBuffer";
  100.                 final Class<?>[] interfaces = buffer.getClass().getInterfaces();
  101.                 final Object[] EMPTY_OBJECT_ARRAY = {};

  102.                 for (final Class<?> clazz : interfaces) {
  103.                     if (clazz.getName().equals(SUN_CLASS)) {
  104.                         /* DirectBuffer#cleaner() */
  105.                         final Method getCleaner = Class.forName(SUN_CLASS).getMethod("cleaner");
  106.                         final Object cleaner = getCleaner.invoke(buffer, EMPTY_OBJECT_ARRAY);
  107.                         /* Cleaner#clean() */
  108.                         final Method cleanMethod = Class.forName("sun.misc.Cleaner").getMethod("clean");
  109.                         cleanMethod.invoke(cleaner, EMPTY_OBJECT_ARRAY);
  110.                         return;
  111.                     }
  112.                 }
  113.             } catch (final ReflectiveOperationException e) { // NOPMD
  114.                 // Ignore the Reflection exception.
  115.             }
  116.         }
  117.     }

  118.     /**
  119.      * Reads crypto buffer size.
  120.      *
  121.      * @param props The {@code Properties} class represents a set of
  122.      *        properties.
  123.      * @return the buffer size.
  124.      * */
  125.     static int getBufferSize(final Properties props) {
  126.         final String bufferSizeStr = props.getProperty(CryptoInputStream.STREAM_BUFFER_SIZE_KEY, "");
  127.         return bufferSizeStr.isEmpty() ? CryptoInputStream.STREAM_BUFFER_SIZE_DEFAULT : Integer.parseInt(bufferSizeStr);
  128.     }

  129.     private final byte[] oneByteBuf = new byte[1];

  130.     /** The CryptoCipher instance. */
  131.     final CryptoCipher cipher; // package protected for access by crypto classes; do not expose further

  132.     /** The buffer size. */
  133.     private final int bufferSize;

  134.     /** Crypto key for the cipher. */
  135.     final Key key; // package protected for access by crypto classes; do not expose further

  136.     /** the algorithm parameters */
  137.     private final AlgorithmParameterSpec params;

  138.     /** Flag to mark whether the input stream is closed. */
  139.     private boolean closed;

  140.     /**
  141.      * Flag to mark whether do final of the cipher to end the decrypting stream.
  142.      */
  143.     private boolean finalDone;

  144.     /** The input data. */
  145.     Input input; // package protected for access by crypto classes; do not expose further

  146.     /**
  147.      * Input data buffer. The data starts at inBuffer.position() and ends at to
  148.      * inBuffer.limit().
  149.      */
  150.     ByteBuffer inBuffer; // package protected for access by crypto classes; do not expose further

  151.     /**
  152.      * The decrypted data buffer. The data starts at outBuffer.position() and
  153.      * ends at outBuffer.limit().
  154.      */
  155.     ByteBuffer outBuffer; // package protected for access by crypto classes; do not expose further

  156.     /**
  157.      * Constructs a {@link CryptoInputStream}.
  158.      *
  159.      * @param input the input data.
  160.      * @param cipher the cipher instance.
  161.      * @param bufferSize the bufferSize.
  162.      * @param key crypto key for the cipher.
  163.      * @param params the algorithm parameters.
  164.      * @throws IOException if an I/O error occurs.
  165.      */
  166.     protected CryptoInputStream(final Input input, final CryptoCipher cipher, final int bufferSize,
  167.             final Key key, final AlgorithmParameterSpec params) throws IOException {
  168.         this.input = input;
  169.         this.cipher = cipher;
  170.         this.bufferSize = CryptoInputStream.checkBufferSize(cipher, bufferSize);

  171.         this.key = key;
  172.         this.params = params;
  173.         if (!(params instanceof IvParameterSpec)) {
  174.             // other AlgorithmParameterSpec such as GCMParameterSpec is not
  175.             // supported now.
  176.             throw new IOException("Illegal parameters");
  177.         }

  178.         inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
  179.         outBuffer = ByteBuffer.allocateDirect(this.bufferSize + cipher.getBlockSize());
  180.         outBuffer.limit(0);

  181.         initCipher();
  182.     }

  183.     /**
  184.      * Constructs a {@link CryptoInputStream}.
  185.      *
  186.      * @param cipher the cipher instance.
  187.      * @param inputStream the input stream.
  188.      * @param bufferSize the bufferSize.
  189.      * @param key crypto key for the cipher.
  190.      * @param params the algorithm parameters.
  191.      * @throws IOException if an I/O error occurs.
  192.      */
  193.     @SuppressWarnings("resource") // Closing the instance closes the StreamInput
  194.     protected CryptoInputStream(final InputStream inputStream, final CryptoCipher cipher,
  195.             final int bufferSize, final Key key, final AlgorithmParameterSpec params)
  196.             throws IOException {
  197.         this(new StreamInput(inputStream, bufferSize), cipher, bufferSize, key, params);
  198.     }

  199.     /**
  200.      * Constructs a {@link CryptoInputStream}.
  201.      *
  202.      * @param channel the ReadableByteChannel instance.
  203.      * @param cipher the cipher instance.
  204.      * @param bufferSize the bufferSize.
  205.      * @param key crypto key for the cipher.
  206.      * @param params the algorithm parameters.
  207.      * @throws IOException if an I/O error occurs.
  208.      */
  209.     @SuppressWarnings("resource") // Closing the instance closes the ChannelInput
  210.     protected CryptoInputStream(final ReadableByteChannel channel, final CryptoCipher cipher,
  211.             final int bufferSize, final Key key, final AlgorithmParameterSpec params)
  212.             throws IOException {
  213.         this(new ChannelInput(channel), cipher, bufferSize, key, params);
  214.     }

  215.     /**
  216.      * Constructs a {@link CryptoInputStream}.
  217.      *
  218.      * @param transformation the name of the transformation, e.g.,
  219.      * <i>AES/CBC/PKCS5Padding</i>.
  220.      * See the Java Cryptography Architecture Standard Algorithm Name Documentation
  221.      * for information about standard transformation names.
  222.      * @param properties The {@code Properties} class represents a set of
  223.      *        properties.
  224.      * @param inputStream the input stream.
  225.      * @param key crypto key for the cipher.
  226.      * @param params the algorithm parameters.
  227.      * @throws IOException if an I/O error occurs.
  228.      */
  229.     @SuppressWarnings("resource") // The CryptoCipher returned by getCipherInstance() is closed by CryptoInputStream.
  230.     public CryptoInputStream(final String transformation,
  231.             final Properties properties, final InputStream inputStream, final Key key,
  232.             final AlgorithmParameterSpec params) throws IOException {
  233.         this(inputStream, Utils.getCipherInstance(transformation, properties),
  234.                 CryptoInputStream.getBufferSize(properties), key, params);
  235.     }

  236.     /**
  237.      * Constructs a {@link CryptoInputStream}.
  238.      *
  239.      * @param transformation the name of the transformation, e.g.,
  240.      * <i>AES/CBC/PKCS5Padding</i>.
  241.      * See the Java Cryptography Architecture Standard Algorithm Name Documentation
  242.      * for information about standard transformation names.
  243.      * @param properties The {@code Properties} class represents a set of
  244.      *        properties.
  245.      * @param channel the ReadableByteChannel object.
  246.      * @param key crypto key for the cipher.
  247.      * @param params the algorithm parameters.
  248.      * @throws IOException if an I/O error occurs.
  249.      */
  250.     @SuppressWarnings("resource") // The CryptoCipher returned by getCipherInstance() is closed by CryptoInputStream.
  251.     public CryptoInputStream(final String transformation,
  252.             final Properties properties, final ReadableByteChannel channel, final Key key,
  253.             final AlgorithmParameterSpec params) throws IOException {
  254.         this(channel, Utils.getCipherInstance(transformation, properties), CryptoInputStream
  255.                 .getBufferSize(properties), key, params);
  256.     }

  257.     /**
  258.      * Overrides the {@link InputStream#available()}. Returns an estimate of the
  259.      * number of bytes that can be read (or skipped over) from this input stream
  260.      * without blocking by the next invocation of a method for this input
  261.      * stream.
  262.      *
  263.      * @return an estimate of the number of bytes that can be read (or skipped
  264.      *         over) from this input stream without blocking or {@code 0} when
  265.      *         it reaches the end of the input stream.
  266.      * @throws IOException if an I/O error occurs.
  267.      */
  268.     @Override
  269.     public int available() throws IOException {
  270.         checkStream();

  271.         return input.available() + outBuffer.remaining();
  272.     }

  273.     /**
  274.      * Checks whether the stream is closed.
  275.      *
  276.      * @throws IOException if an I/O error occurs.
  277.      */
  278.     protected void checkStream() throws IOException {
  279.         if (closed) {
  280.             throw new IOException("Stream closed");
  281.         }
  282.     }

  283.     /**
  284.      * Overrides the {@link InputStream#close()}. Closes this input stream and
  285.      * releases any system resources associated with the stream.
  286.      *
  287.      * @throws IOException if an I/O error occurs.
  288.      */
  289.     @Override
  290.     public void close() throws IOException {
  291.         if (closed) {
  292.             return;
  293.         }

  294.         input.close();
  295.         freeBuffers();
  296.         cipher.close();
  297.         super.close();
  298.         closed = true;
  299.     }

  300.     /**
  301.      * Does the decryption using inBuffer as input and outBuffer as output. Upon
  302.      * return, inBuffer is cleared; the decrypted data starts at
  303.      * outBuffer.position() and ends at outBuffer.limit().
  304.      *
  305.      * @throws IOException if an I/O error occurs.
  306.      */
  307.     protected void decrypt() throws IOException {
  308.         // Prepare the input buffer and clear the out buffer
  309.         inBuffer.flip();
  310.         outBuffer.clear();

  311.         try {
  312.             cipher.update(inBuffer, outBuffer);
  313.         } catch (final ShortBufferException e) {
  314.             throw new IOException(e);
  315.         }

  316.         // Clear the input buffer and prepare out buffer
  317.         inBuffer.clear();
  318.         outBuffer.flip();
  319.     }

  320.     /**
  321.      * Does final of the cipher to end the decrypting stream.
  322.      *
  323.      * @throws IOException if an I/O error occurs.
  324.      */
  325.     protected void decryptFinal() throws IOException {
  326.         // Prepare the input buffer and clear the out buffer
  327.         inBuffer.flip();
  328.         outBuffer.clear();

  329.         try {
  330.             cipher.doFinal(inBuffer, outBuffer);
  331.             finalDone = true;
  332.         } catch (final ShortBufferException | IllegalBlockSizeException | BadPaddingException e) {
  333.             throw new IOException(e);
  334.         }

  335.         // Clear the input buffer and prepare out buffer
  336.         inBuffer.clear();
  337.         outBuffer.flip();
  338.     }

  339.     /**
  340.      * Decrypts more data by reading the under layer stream. The decrypted data
  341.      * will be put in the output buffer. If the end of the under stream reached,
  342.      * we will do final of the cipher to finish all the decrypting of data.
  343.      *
  344.      * @return The number of decrypted data.
  345.      *           return -1 (if end of the decrypted stream)
  346.      *           return 0 (no data now, but could have more later)
  347.      * @throws IOException if an I/O error occurs.
  348.      */
  349.     protected int decryptMore() throws IOException {
  350.         if (finalDone) {
  351.             return EOS;
  352.         }

  353.         final int n = input.read(inBuffer);
  354.         if (n < 0) {
  355.             // The stream is end, finalize the cipher stream
  356.             decryptFinal();

  357.             // Satisfy the read with the remaining
  358.             final int remaining = outBuffer.remaining();
  359.             if (remaining > 0) {
  360.                 return remaining;
  361.             }

  362.             // End of the stream
  363.             return EOS;
  364.         }
  365.         if (n == 0) {
  366.             // No data is read, but the stream is not end yet
  367.             return 0;
  368.         }
  369.         decrypt();
  370.         return outBuffer.remaining();
  371.     }

  372.     /** Forcibly free the direct buffers. */
  373.     protected void freeBuffers() {
  374.         CryptoInputStream.freeDirectBuffer(inBuffer);
  375.         CryptoInputStream.freeDirectBuffer(outBuffer);
  376.     }

  377.     /**
  378.      * Gets the buffer size.
  379.      *
  380.      * @return the bufferSize.
  381.      */
  382.     protected int getBufferSize() {
  383.         return bufferSize;
  384.     }

  385.     /**
  386.      * Gets the internal CryptoCipher.
  387.      *
  388.      * @return the cipher instance.
  389.      */
  390.     protected CryptoCipher getCipher() {
  391.         return cipher;
  392.     }

  393.     /**
  394.      * Gets the input.
  395.      *
  396.      * @return the input.
  397.      */
  398.     protected Input getInput() {
  399.         return input;
  400.     }

  401.     /**
  402.      * Gets the key.
  403.      *
  404.      * @return the key.
  405.      */
  406.     protected Key getKey() {
  407.         return key;
  408.     }

  409.     /**
  410.      * Gets the specification of cryptographic parameters.
  411.      *
  412.      * @return the params.
  413.      */
  414.     protected AlgorithmParameterSpec getParams() {
  415.         return params;
  416.     }

  417.     /**
  418.      * Initializes the cipher.
  419.      *
  420.      * @throws IOException if an I/O error occurs.
  421.      */
  422.     protected void initCipher() throws IOException {
  423.         try {
  424.             cipher.init(Cipher.DECRYPT_MODE, key, params);
  425.         } catch (final GeneralSecurityException e) {
  426.             throw new IOException(e);
  427.         }
  428.     }

  429.     /**
  430.      * Overrides the {@link java.nio.channels.Channel#isOpen()}.
  431.      *
  432.      * @return {@code true} if, and only if, this channel is open.
  433.      */
  434.     @Override
  435.     public boolean isOpen() {
  436.         return !closed;
  437.     }

  438.     /**
  439.      * Overrides the {@link InputStream#markSupported()}.
  440.      *
  441.      * @return false,the {@link CtrCryptoInputStream} don't support the mark
  442.      *         method.
  443.      */
  444.     @Override
  445.     public boolean markSupported() {
  446.         return false;
  447.     }

  448.     /**
  449.      * Overrides the {@link java.io.InputStream#read()}. Reads the next byte of
  450.      * data from the input stream.
  451.      *
  452.      * @return the next byte of data, or {@code EOS (-1)} if the end of the
  453.      *         stream is reached.
  454.      * @throws IOException if an I/O error occurs.
  455.      */
  456.     @Override
  457.     public int read() throws IOException {
  458.         int n;
  459.         while ((n = read(oneByteBuf, 0, 1)) == 0) { //NOPMD
  460.             /* no op */
  461.         }
  462.         return n == EOS ? EOS : oneByteBuf[0] & 0xff;
  463.     }

  464.     /**
  465.      * Overrides the {@link java.io.InputStream#read(byte[], int, int)}.
  466.      * Decryption is buffer based. If there is data in {@link #outBuffer}, then
  467.      * read it out of this buffer. If there is no data in {@link #outBuffer},
  468.      * then read more from the underlying stream and do the decryption.
  469.      *
  470.      * @param array the buffer into which the decrypted data is read.
  471.      * @param off the buffer offset.
  472.      * @param len the maximum number of decrypted data bytes to read.
  473.      * @return int the total number of decrypted data bytes read into the
  474.      *         buffer.
  475.      * @throws IOException if an I/O error occurs.
  476.      */
  477.     @Override
  478.     public int read(final byte[] array, final int off, final int len) throws IOException {
  479.         checkStream();
  480.         Objects.requireNonNull(array, "array");
  481.         if (off < 0 || len < 0 || len > array.length - off) {
  482.             throw new IndexOutOfBoundsException();
  483.         }
  484.         if (len == 0) {
  485.             return 0;
  486.         }

  487.         final int remaining = outBuffer.remaining();
  488.         if (remaining > 0) {
  489.             // Satisfy the read with the existing data
  490.             final int n = Math.min(len, remaining);
  491.             outBuffer.get(array, off, n);
  492.             return n;
  493.         }
  494.         // No data in the out buffer, try read new data and decrypt it
  495.         // we loop for new data
  496.         int nd = 0;
  497.         while (nd == 0) {
  498.             nd = decryptMore();
  499.         }
  500.         if (nd < 0) {
  501.             return nd;
  502.         }

  503.         final int n = Math.min(len, outBuffer.remaining());
  504.         outBuffer.get(array, off, n);
  505.         return n;
  506.     }

  507.     /**
  508.      * Overrides the
  509.      * {@link java.nio.channels.ReadableByteChannel#read(ByteBuffer)}. Reads a
  510.      * sequence of bytes from this channel into the given buffer.
  511.      *
  512.      * @param dst The buffer into which bytes are to be transferred.
  513.      * @return The number of bytes read, possibly zero, or {@code EOS (-1)} if the
  514.      *         channel has reached end-of-stream.
  515.      * @throws IOException if an I/O error occurs.
  516.      */
  517.     @Override
  518.     public int read(final ByteBuffer dst) throws IOException {
  519.         checkStream();
  520.         int remaining = outBuffer.remaining();
  521.         if (remaining <= 0) {
  522.             // Decrypt more data
  523.             // we loop for new data
  524.             int nd = 0;
  525.             while (nd == 0) {
  526.                 nd = decryptMore();
  527.             }

  528.             if (nd < 0) {
  529.                 return EOS;
  530.             }
  531.         }

  532.         // Copy decrypted data from outBuffer to dst
  533.         remaining = outBuffer.remaining();
  534.         final int toRead = dst.remaining();
  535.         if (toRead <= remaining) {
  536.             final int limit = outBuffer.limit();
  537.             outBuffer.limit(outBuffer.position() + toRead);
  538.             dst.put(outBuffer);
  539.             outBuffer.limit(limit);
  540.             return toRead;
  541.         }
  542.         dst.put(outBuffer);
  543.         return remaining;
  544.     }

  545.     /**
  546.      * Overrides the {@link java.io.InputStream#skip(long)}. Skips over and
  547.      * discards {@code n} bytes of data from this input stream.
  548.      *
  549.      * @param n the number of bytes to be skipped.
  550.      * @return the actual number of bytes skipped.
  551.      * @throws IOException if an I/O error occurs.
  552.      */
  553.     @Override
  554.     public long skip(final long n) throws IOException {
  555.         Utils.checkArgument(n >= 0, "Negative skip length.");
  556.         checkStream();

  557.         if (n == 0) {
  558.             return 0;
  559.         }

  560.         long remaining = n;
  561.         int nd;

  562.         while (remaining > 0) {
  563.             if (remaining <= outBuffer.remaining()) {
  564.                 // Skip in the remaining buffer
  565.                 final int pos = outBuffer.position() + (int) remaining;
  566.                 outBuffer.position(pos);

  567.                 remaining = 0;
  568.                 break;
  569.             }
  570.             remaining -= outBuffer.remaining();
  571.             outBuffer.clear();

  572.             // we loop for new data
  573.             nd = 0;
  574.             while (nd == 0) {
  575.                 nd = decryptMore();
  576.             }
  577.             if (nd < 0) {
  578.                 break;
  579.             }
  580.         }

  581.         return n - remaining;
  582.     }
  583. }