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 }