View Javadoc
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  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.security.MessageDigest;
22  import java.security.NoSuchAlgorithmException;
23  import java.security.Provider;
24  import java.util.Arrays;
25  import java.util.Objects;
26  
27  /**
28   * Calculates a checksum using a {@link MessageDigest}, for example, a SHA-512 sum.
29   * <p>
30   * To build an instance, use {@link Builder}.
31   * </p>
32   * <p>
33   * See the MessageDigest section in the <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java
34   * Cryptography Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names.
35   * </p>
36   * <p>
37   * <em>Note</em>: Neither {@link ObservableInputStream}, nor {@link MessageDigest}, are thread safe, so is {@link MessageDigestCalculatingInputStream}.
38   * </p>
39   *
40   * @see Builder
41   * @deprecated Use {@link MessageDigestInputStream}.
42   */
43  @Deprecated
44  public class MessageDigestCalculatingInputStream extends ObservableInputStream {
45  
46      // @formatter:off
47      /**
48       * Builds a new {@link MessageDigestCalculatingInputStream}.
49       *
50       * <p>
51       * For example:
52       * </p>
53       * <pre>{@code
54       * MessageDigestCalculatingInputStream s = MessageDigestCalculatingInputStream.builder()
55       *   .setPath(path)
56       *   .setMessageDigest("SHA-512")
57       *   .get();}
58       * </pre>
59       * <p>
60       * <em>The MD5 cryptographic algorithm is weak and should not be used.</em>
61       * </p>
62       *
63       * @see #get()
64       * @since 2.12.0
65       */
66      // @formatter:on
67      public static class Builder extends AbstractBuilder<Builder> {
68  
69          private MessageDigest messageDigest;
70  
71          /**
72           * Constructs a new builder of {@link MessageDigestCalculatingInputStream}.
73           * <p>
74           * The default for compatibility is the MD5 cryptographic algorithm which is weak and should not be used.
75           * </p>
76           */
77          public Builder() {
78              try {
79                  this.messageDigest = getDefaultMessageDigest();
80              } catch (final NoSuchAlgorithmException e) {
81                  // Should not happen.
82                  throw new IllegalStateException(e);
83              }
84          }
85  
86          /**
87           * Builds a new {@link MessageDigestCalculatingInputStream}.
88           * <p>
89           * You must set an aspect that supports {@link #getInputStream()}, otherwise, this method throws an exception.
90           * </p>
91           * <p>
92           * This builder uses the following aspects:
93           * </p>
94           * <ul>
95           * <li>{@link #getInputStream()} gets the target aspect.</li>
96           * <li>{@link MessageDigest}</li>
97           * </ul>
98           *
99           * @return a new instance.
100          * @throws NullPointerException if messageDigest is null.
101          * @throws IllegalStateException         if the {@code origin} is {@code null}.
102          * @throws UnsupportedOperationException if the origin cannot be converted to an {@link InputStream}.
103          * @throws IOException                   if an I/O error occurs converting to an {@link InputStream} using {@link #getInputStream()}.
104          * @see #getInputStream()
105          * @see #getUnchecked()
106          */
107         @Override
108         public MessageDigestCalculatingInputStream get() throws IOException {
109             setObservers(Arrays.asList(new MessageDigestMaintainingObserver(messageDigest)));
110             return new MessageDigestCalculatingInputStream(this);
111         }
112 
113         /**
114          * Sets the message digest.
115          * <p>
116          * <em>The MD5 cryptographic algorithm is weak and should not be used.</em>
117          * </p>
118          *
119          * @param messageDigest the message digest.
120          */
121         public void setMessageDigest(final MessageDigest messageDigest) {
122             this.messageDigest = messageDigest;
123         }
124 
125         /**
126          * Sets the name of the name of the message digest algorithm.
127          * <p>
128          * <em>The MD5 cryptographic algorithm is weak and should not be used.</em>
129          * </p>
130          *
131          * @param algorithm the name of the algorithm. See the MessageDigest section in the
132          *                  <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java Cryptography
133          *                  Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names.
134          * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified algorithm.
135          */
136         public void setMessageDigest(final String algorithm) throws NoSuchAlgorithmException {
137             this.messageDigest = MessageDigest.getInstance(algorithm);
138         }
139 
140     }
141 
142     /**
143      * Maintains the message digest.
144      */
145     public static class MessageDigestMaintainingObserver extends Observer {
146         private final MessageDigest messageDigest;
147 
148         /**
149          * Constructs an MessageDigestMaintainingObserver for the given MessageDigest.
150          *
151          * @param messageDigest the message digest to use
152          * @throws NullPointerException if messageDigest is null.
153          */
154         public MessageDigestMaintainingObserver(final MessageDigest messageDigest) {
155             this.messageDigest = Objects.requireNonNull(messageDigest, "messageDigest");
156         }
157 
158         @Override
159         public void data(final byte[] input, final int offset, final int length) throws IOException {
160             messageDigest.update(input, offset, length);
161         }
162 
163         @Override
164         public void data(final int input) throws IOException {
165             messageDigest.update((byte) input);
166         }
167     }
168 
169     /**
170      * The default message digest algorithm {@code "MD5"}.
171      * <p>
172      * The MD5 cryptographic algorithm is weak and should not be used.
173      * </p>
174      */
175     private static final String DEFAULT_ALGORITHM = "MD5";
176 
177     /**
178      * Constructs a new {@link Builder}.
179      *
180      * @return a new {@link Builder}.
181      * @since 2.12.0
182      */
183     public static Builder builder() {
184         return new Builder();
185     }
186 
187     /**
188      * Gets a MessageDigest object that implements the default digest algorithm {@code "MD5"}.
189      * <p>
190      * The MD5 cryptographic algorithm is weak and should not be used.
191      * </p>
192      *
193      * @return a Message Digest object that implements the default algorithm.
194      * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation.
195      * @see Provider
196      */
197     static MessageDigest getDefaultMessageDigest() throws NoSuchAlgorithmException {
198         return MessageDigest.getInstance(DEFAULT_ALGORITHM);
199     }
200 
201     private final MessageDigest messageDigest;
202 
203     private MessageDigestCalculatingInputStream(final Builder builder) throws IOException {
204         super(builder);
205         this.messageDigest = builder.messageDigest;
206     }
207 
208     /**
209      * Constructs a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the "MD5" algorithm.
210      * <p>
211      * The MD5 algorithm is weak and should not be used.
212      * </p>
213      *
214      * @param inputStream the stream to calculate the message digest for
215      * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified algorithm.
216      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
217      */
218     @Deprecated
219     public MessageDigestCalculatingInputStream(final InputStream inputStream) throws NoSuchAlgorithmException {
220         this(inputStream, getDefaultMessageDigest());
221     }
222 
223     /**
224      * Constructs a new instance, which calculates a signature on the given stream, using the given {@link MessageDigest}.
225      * <p>
226      * The MD5 cryptographic algorithm is weak and should not be used.
227      * </p>
228      *
229      * @param inputStream   the stream to calculate the message digest for
230      * @param messageDigest the message digest to use
231      * @throws NullPointerException if messageDigest is null.
232      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
233      */
234     @Deprecated
235     public MessageDigestCalculatingInputStream(final InputStream inputStream, final MessageDigest messageDigest) {
236         super(inputStream, new MessageDigestMaintainingObserver(messageDigest));
237         this.messageDigest = messageDigest;
238     }
239 
240     /**
241      * Constructs a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the given algorithm.
242      * <p>
243      * The MD5 cryptographic algorithm is weak and should not be used.
244      * </p>
245      *
246      * @param inputStream the stream to calculate the message digest for
247      * @param algorithm   the name of the algorithm requested. See the MessageDigest section in the
248      *                    <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java Cryptography
249      *                    Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names.
250      * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified algorithm.
251      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
252      */
253     @Deprecated
254     public MessageDigestCalculatingInputStream(final InputStream inputStream, final String algorithm) throws NoSuchAlgorithmException {
255         this(inputStream, MessageDigest.getInstance(algorithm));
256     }
257 
258     /**
259      * Gets the {@link MessageDigest}, which is being used for generating the checksum.
260      * <p>
261      * <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
262      * data has been read, if that is what you want. The easiest way to do so is by invoking {@link #consume()}.
263      * </p>
264      *
265      * @return the message digest used
266      */
267     public MessageDigest getMessageDigest() {
268         return messageDigest;
269     }
270 }