001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.io.input; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.security.MessageDigest; 022import java.security.NoSuchAlgorithmException; 023import java.security.Provider; 024import java.util.Arrays; 025import java.util.Objects; 026 027/** 028 * Calculates a checksum using a {@link MessageDigest}, for example, a SHA-512 sum. 029 * <p> 030 * To build an instance, use {@link Builder}. 031 * </p> 032 * <p> 033 * See the MessageDigest section in the <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java 034 * Cryptography Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names. 035 * </p> 036 * <p> 037 * <em>Note</em>: Neither {@link ObservableInputStream}, nor {@link MessageDigest}, are thread safe, so is {@link MessageDigestCalculatingInputStream}. 038 * </p> 039 * 040 * @see Builder 041 * @deprecated Use {@link MessageDigestInputStream}. 042 */ 043@Deprecated 044public class MessageDigestCalculatingInputStream extends ObservableInputStream { 045 046 // @formatter:off 047 /** 048 * Builds a new {@link MessageDigestCalculatingInputStream}. 049 * 050 * <p> 051 * For example: 052 * </p> 053 * <pre>{@code 054 * MessageDigestCalculatingInputStream s = MessageDigestCalculatingInputStream.builder() 055 * .setPath(path) 056 * .setMessageDigest("SHA-512") 057 * .get();} 058 * </pre> 059 * <p> 060 * <em>The MD5 cryptographic algorithm is weak and should not be used.</em> 061 * </p> 062 * 063 * @see #get() 064 * @since 2.12.0 065 */ 066 // @formatter:on 067 public static class Builder extends AbstractBuilder<Builder> { 068 069 private MessageDigest messageDigest; 070 071 /** 072 * Constructs a new {@link Builder}. 073 * <p> 074 * The default for compatibility is the MD5 cryptographic algorithm which is weak and should not be used. 075 * </p> 076 */ 077 public Builder() { 078 try { 079 this.messageDigest = getDefaultMessageDigest(); 080 } catch (final NoSuchAlgorithmException e) { 081 // Should not happen. 082 throw new IllegalStateException(e); 083 } 084 } 085 086 /** 087 * Builds a new {@link MessageDigestCalculatingInputStream}. 088 * <p> 089 * You must set input that supports {@link #getInputStream()}, otherwise, this method throws an exception. 090 * </p> 091 * <p> 092 * This builder use the following aspects: 093 * </p> 094 * <ul> 095 * <li>{@link #getPath()}</li> 096 * <li>{@link MessageDigest}</li> 097 * </ul> 098 * 099 * @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. 104 * @see #getInputStream() 105 */ 106 @Override 107 public MessageDigestCalculatingInputStream get() throws IOException { 108 setObservers(Arrays.asList(new MessageDigestMaintainingObserver(messageDigest))); 109 return new MessageDigestCalculatingInputStream(this); 110 } 111 112 /** 113 * Sets the message digest. 114 * <p> 115 * <em>The MD5 cryptographic algorithm is weak and should not be used.</em> 116 * </p> 117 * 118 * @param messageDigest the message digest. 119 */ 120 public void setMessageDigest(final MessageDigest messageDigest) { 121 this.messageDigest = messageDigest; 122 } 123 124 /** 125 * Sets the name of the name of the message digest algorithm. 126 * <p> 127 * <em>The MD5 cryptographic algorithm is weak and should not be used.</em> 128 * </p> 129 * 130 * @param algorithm the name of the algorithm. See the MessageDigest section in the 131 * <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java Cryptography 132 * Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names. 133 * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified algorithm. 134 */ 135 public void setMessageDigest(final String algorithm) throws NoSuchAlgorithmException { 136 this.messageDigest = MessageDigest.getInstance(algorithm); 137 } 138 139 } 140 141 /** 142 * Maintains the message digest. 143 */ 144 public static class MessageDigestMaintainingObserver extends Observer { 145 private final MessageDigest messageDigest; 146 147 /** 148 * Constructs an MessageDigestMaintainingObserver for the given MessageDigest. 149 * 150 * @param messageDigest the message digest to use 151 * @throws NullPointerException if messageDigest is null. 152 */ 153 public MessageDigestMaintainingObserver(final MessageDigest messageDigest) { 154 this.messageDigest = Objects.requireNonNull(messageDigest, "messageDigest"); 155 } 156 157 @Override 158 public void data(final byte[] input, final int offset, final int length) throws IOException { 159 messageDigest.update(input, offset, length); 160 } 161 162 @Override 163 public void data(final int input) throws IOException { 164 messageDigest.update((byte) input); 165 } 166 } 167 168 /** 169 * The default message digest algorithm {@code "MD5"}. 170 * <p> 171 * The MD5 cryptographic algorithm is weak and should not be used. 172 * </p> 173 */ 174 private static final String DEFAULT_ALGORITHM = "MD5"; 175 176 /** 177 * Constructs a new {@link Builder}. 178 * 179 * @return a new {@link Builder}. 180 * @since 2.12.0 181 */ 182 public static Builder builder() { 183 return new Builder(); 184 } 185 186 /** 187 * Gets a MessageDigest object that implements the default digest algorithm {@code "MD5"}. 188 * <p> 189 * The MD5 cryptographic algorithm is weak and should not be used. 190 * </p> 191 * 192 * @return a Message Digest object that implements the default algorithm. 193 * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation. 194 * @see Provider 195 */ 196 static MessageDigest getDefaultMessageDigest() throws NoSuchAlgorithmException { 197 return MessageDigest.getInstance(DEFAULT_ALGORITHM); 198 } 199 200 private final MessageDigest messageDigest; 201 202 private MessageDigestCalculatingInputStream(final Builder builder) throws IOException { 203 super(builder); 204 this.messageDigest = builder.messageDigest; 205 } 206 207 /** 208 * Constructs a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the "MD5" algorithm. 209 * <p> 210 * The MD5 algorithm is weak and should not be used. 211 * </p> 212 * 213 * @param inputStream the stream to calculate the message digest for 214 * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified algorithm. 215 * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}. 216 */ 217 @Deprecated 218 public MessageDigestCalculatingInputStream(final InputStream inputStream) throws NoSuchAlgorithmException { 219 this(inputStream, getDefaultMessageDigest()); 220 } 221 222 /** 223 * Constructs a new instance, which calculates a signature on the given stream, using the given {@link MessageDigest}. 224 * <p> 225 * The MD5 cryptographic algorithm is weak and should not be used. 226 * </p> 227 * 228 * @param inputStream the stream to calculate the message digest for 229 * @param messageDigest the message digest to use 230 * @throws NullPointerException if messageDigest is null. 231 * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}. 232 */ 233 @Deprecated 234 public MessageDigestCalculatingInputStream(final InputStream inputStream, final MessageDigest messageDigest) { 235 super(inputStream, new MessageDigestMaintainingObserver(messageDigest)); 236 this.messageDigest = messageDigest; 237 } 238 239 /** 240 * Constructs a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the given algorithm. 241 * <p> 242 * The MD5 cryptographic algorithm is weak and should not be used. 243 * </p> 244 * 245 * @param inputStream the stream to calculate the message digest for 246 * @param algorithm the name of the algorithm requested. See the MessageDigest section in the 247 * <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java Cryptography 248 * Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names. 249 * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified algorithm. 250 * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}. 251 */ 252 @Deprecated 253 public MessageDigestCalculatingInputStream(final InputStream inputStream, final String algorithm) throws NoSuchAlgorithmException { 254 this(inputStream, MessageDigest.getInstance(algorithm)); 255 } 256 257 /** 258 * Gets the {@link MessageDigest}, which is being used for generating the checksum. 259 * <p> 260 * <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 261 * data has been read, if that is what you want. The easiest way to do so is by invoking {@link #consume()}. 262 * </p> 263 * 264 * @return the message digest used 265 */ 266 public MessageDigest getMessageDigest() { 267 return messageDigest; 268 } 269}