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 builder of {@link MessageDigestCalculatingInputStream}. 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 an aspect that supports {@link #getInputStream()}, otherwise, this method throws an exception. 090 * </p> 091 * <p> 092 * This builder uses the following aspects: 093 * </p> 094 * <ul> 095 * <li>{@link #getInputStream()} gets the target aspect.</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 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}