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