MessageDigestCalculatingInputStream.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.io.input;

  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.security.MessageDigest;
  21. import java.security.NoSuchAlgorithmException;
  22. import java.security.Provider;
  23. import java.util.Arrays;
  24. import java.util.Objects;

  25. /**
  26.  * Calculates a checksum using a {@link MessageDigest}, for example, a SHA-512 sum.
  27.  * <p>
  28.  * To build an instance, use {@link Builder}.
  29.  * </p>
  30.  * <p>
  31.  * See the MessageDigest section in the <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java
  32.  * Cryptography Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names.
  33.  * </p>
  34.  * <p>
  35.  * <em>Note</em>: Neither {@link ObservableInputStream}, nor {@link MessageDigest}, are thread safe, so is {@link MessageDigestCalculatingInputStream}.
  36.  * </p>
  37.  *
  38.  * @see Builder
  39.  * @deprecated Use {@link MessageDigestInputStream}.
  40.  */
  41. @Deprecated
  42. public class MessageDigestCalculatingInputStream extends ObservableInputStream {

  43.     // @formatter:off
  44.     /**
  45.      * Builds a new {@link MessageDigestCalculatingInputStream}.
  46.      *
  47.      * <p>
  48.      * For example:
  49.      * </p>
  50.      * <pre>{@code
  51.      * MessageDigestCalculatingInputStream s = MessageDigestCalculatingInputStream.builder()
  52.      *   .setPath(path)
  53.      *   .setMessageDigest("SHA-512")
  54.      *   .get();}
  55.      * </pre>
  56.      * <p>
  57.      * <em>The MD5 cryptographic algorithm is weak and should not be used.</em>
  58.      * </p>
  59.      *
  60.      * @see #get()
  61.      * @since 2.12.0
  62.      */
  63.     // @formatter:on
  64.     public static class Builder extends AbstractBuilder<Builder> {

  65.         private MessageDigest messageDigest;

  66.         /**
  67.          * Constructs a new builder of {@link MessageDigestCalculatingInputStream}.
  68.          * <p>
  69.          * The default for compatibility is the MD5 cryptographic algorithm which is weak and should not be used.
  70.          * </p>
  71.          */
  72.         public Builder() {
  73.             try {
  74.                 this.messageDigest = getDefaultMessageDigest();
  75.             } catch (final NoSuchAlgorithmException e) {
  76.                 // Should not happen.
  77.                 throw new IllegalStateException(e);
  78.             }
  79.         }

  80.         /**
  81.          * Builds a new {@link MessageDigestCalculatingInputStream}.
  82.          * <p>
  83.          * You must set an aspect that supports {@link #getInputStream()}, otherwise, this method throws an exception.
  84.          * </p>
  85.          * <p>
  86.          * This builder uses the following aspects:
  87.          * </p>
  88.          * <ul>
  89.          * <li>{@link #getInputStream()} gets the target aspect.</li>
  90.          * <li>{@link MessageDigest}</li>
  91.          * </ul>
  92.          *
  93.          * @return a new instance.
  94.          * @throws NullPointerException if messageDigest is null.
  95.          * @throws IllegalStateException         if the {@code origin} is {@code null}.
  96.          * @throws UnsupportedOperationException if the origin cannot be converted to an {@link InputStream}.
  97.          * @throws IOException                   if an I/O error occurs converting to an {@link InputStream} using {@link #getInputStream()}.
  98.          * @see #getInputStream()
  99.          * @see #getUnchecked()
  100.          */
  101.         @Override
  102.         public MessageDigestCalculatingInputStream get() throws IOException {
  103.             setObservers(Arrays.asList(new MessageDigestMaintainingObserver(messageDigest)));
  104.             return new MessageDigestCalculatingInputStream(this);
  105.         }

  106.         /**
  107.          * Sets the message digest.
  108.          * <p>
  109.          * <em>The MD5 cryptographic algorithm is weak and should not be used.</em>
  110.          * </p>
  111.          *
  112.          * @param messageDigest the message digest.
  113.          */
  114.         public void setMessageDigest(final MessageDigest messageDigest) {
  115.             this.messageDigest = messageDigest;
  116.         }

  117.         /**
  118.          * Sets the name of the name of the message digest algorithm.
  119.          * <p>
  120.          * <em>The MD5 cryptographic algorithm is weak and should not be used.</em>
  121.          * </p>
  122.          *
  123.          * @param algorithm the name of the algorithm. See the MessageDigest section in the
  124.          *                  <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java Cryptography
  125.          *                  Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names.
  126.          * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified algorithm.
  127.          */
  128.         public void setMessageDigest(final String algorithm) throws NoSuchAlgorithmException {
  129.             this.messageDigest = MessageDigest.getInstance(algorithm);
  130.         }

  131.     }

  132.     /**
  133.      * Maintains the message digest.
  134.      */
  135.     public static class MessageDigestMaintainingObserver extends Observer {
  136.         private final MessageDigest messageDigest;

  137.         /**
  138.          * Constructs an MessageDigestMaintainingObserver for the given MessageDigest.
  139.          *
  140.          * @param messageDigest the message digest to use
  141.          * @throws NullPointerException if messageDigest is null.
  142.          */
  143.         public MessageDigestMaintainingObserver(final MessageDigest messageDigest) {
  144.             this.messageDigest = Objects.requireNonNull(messageDigest, "messageDigest");
  145.         }

  146.         @Override
  147.         public void data(final byte[] input, final int offset, final int length) throws IOException {
  148.             messageDigest.update(input, offset, length);
  149.         }

  150.         @Override
  151.         public void data(final int input) throws IOException {
  152.             messageDigest.update((byte) input);
  153.         }
  154.     }

  155.     /**
  156.      * The default message digest algorithm {@code "MD5"}.
  157.      * <p>
  158.      * The MD5 cryptographic algorithm is weak and should not be used.
  159.      * </p>
  160.      */
  161.     private static final String DEFAULT_ALGORITHM = "MD5";

  162.     /**
  163.      * Constructs a new {@link Builder}.
  164.      *
  165.      * @return a new {@link Builder}.
  166.      * @since 2.12.0
  167.      */
  168.     public static Builder builder() {
  169.         return new Builder();
  170.     }

  171.     /**
  172.      * Gets a MessageDigest object that implements the default digest algorithm {@code "MD5"}.
  173.      * <p>
  174.      * The MD5 cryptographic algorithm is weak and should not be used.
  175.      * </p>
  176.      *
  177.      * @return a Message Digest object that implements the default algorithm.
  178.      * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation.
  179.      * @see Provider
  180.      */
  181.     static MessageDigest getDefaultMessageDigest() throws NoSuchAlgorithmException {
  182.         return MessageDigest.getInstance(DEFAULT_ALGORITHM);
  183.     }

  184.     private final MessageDigest messageDigest;

  185.     private MessageDigestCalculatingInputStream(final Builder builder) throws IOException {
  186.         super(builder);
  187.         this.messageDigest = builder.messageDigest;
  188.     }

  189.     /**
  190.      * Constructs a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the "MD5" algorithm.
  191.      * <p>
  192.      * The MD5 algorithm is weak and should not be used.
  193.      * </p>
  194.      *
  195.      * @param inputStream the stream to calculate the message digest for
  196.      * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified algorithm.
  197.      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
  198.      */
  199.     @Deprecated
  200.     public MessageDigestCalculatingInputStream(final InputStream inputStream) throws NoSuchAlgorithmException {
  201.         this(inputStream, getDefaultMessageDigest());
  202.     }

  203.     /**
  204.      * Constructs a new instance, which calculates a signature on the given stream, using the given {@link MessageDigest}.
  205.      * <p>
  206.      * The MD5 cryptographic algorithm is weak and should not be used.
  207.      * </p>
  208.      *
  209.      * @param inputStream   the stream to calculate the message digest for
  210.      * @param messageDigest the message digest to use
  211.      * @throws NullPointerException if messageDigest is null.
  212.      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
  213.      */
  214.     @Deprecated
  215.     public MessageDigestCalculatingInputStream(final InputStream inputStream, final MessageDigest messageDigest) {
  216.         super(inputStream, new MessageDigestMaintainingObserver(messageDigest));
  217.         this.messageDigest = messageDigest;
  218.     }

  219.     /**
  220.      * Constructs a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the given algorithm.
  221.      * <p>
  222.      * The MD5 cryptographic algorithm is weak and should not be used.
  223.      * </p>
  224.      *
  225.      * @param inputStream the stream to calculate the message digest for
  226.      * @param algorithm   the name of the algorithm requested. See the MessageDigest section in the
  227.      *                    <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java Cryptography
  228.      *                    Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names.
  229.      * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified algorithm.
  230.      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
  231.      */
  232.     @Deprecated
  233.     public MessageDigestCalculatingInputStream(final InputStream inputStream, final String algorithm) throws NoSuchAlgorithmException {
  234.         this(inputStream, MessageDigest.getInstance(algorithm));
  235.     }

  236.     /**
  237.      * Gets the {@link MessageDigest}, which is being used for generating the checksum.
  238.      * <p>
  239.      * <em>Note</em>: The checksum will only reflect the data, which has been read so far. This is probably not, what you expect. Make sure, that the complete
  240.      * data has been read, if that is what you want. The easiest way to do so is by invoking {@link #consume()}.
  241.      * </p>
  242.      *
  243.      * @return the message digest used
  244.      */
  245.     public MessageDigest getMessageDigest() {
  246.         return messageDigest;
  247.     }
  248. }