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 * https://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 }