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 }