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