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.Objects; 025 026import org.apache.commons.io.build.AbstractStreamBuilder; 027 028/** 029 * This class is an example for using an {@link ObservableInputStream}. It creates its own {@link org.apache.commons.io.input.ObservableInputStream.Observer}, 030 * which calculates a checksum using a {@link MessageDigest}, for example, a SHA-512 sum. 031 * <p> 032 * To build an instance, use {@link Builder}. 033 * </p> 034 * <p> 035 * See the MessageDigest section in the <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java 036 * Cryptography Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names. 037 * </p> 038 * <p> 039 * <em>Note</em>: Neither {@link ObservableInputStream}, nor {@link MessageDigest}, are thread safe, so is {@link MessageDigestCalculatingInputStream}. 040 * </p> 041 * 042 * @see Builder 043 * @deprecated Use {@link MessageDigestInputStream}. 044 */ 045@Deprecated 046public class MessageDigestCalculatingInputStream extends ObservableInputStream { 047 048 // @formatter:off 049 /** 050 * Builds a new {@link MessageDigestCalculatingInputStream}. 051 * 052 * <p> 053 * For example: 054 * </p> 055 * <pre>{@code 056 * MessageDigestCalculatingInputStream s = MessageDigestCalculatingInputStream.builder() 057 * .setPath(path) 058 * .setMessageDigest("SHA-512") 059 * .get();} 060 * </pre> 061 * 062 * @see #get() 063 * @since 2.12.0 064 */ 065 // @formatter:on 066 public static class Builder extends AbstractStreamBuilder<MessageDigestCalculatingInputStream, Builder> { 067 068 private MessageDigest messageDigest; 069 070 /** 071 * Constructs a new {@link Builder}. 072 */ 073 public Builder() { 074 try { 075 this.messageDigest = getDefaultMessageDigest(); 076 } catch (final NoSuchAlgorithmException e) { 077 // Should not happen. 078 throw new IllegalStateException(e); 079 } 080 } 081 082 /** 083 * Builds a new {@link MessageDigestCalculatingInputStream}. 084 * <p> 085 * You must set input that supports {@link #getInputStream()}, otherwise, this method throws an exception. 086 * </p> 087 * <p> 088 * This builder use the following aspects: 089 * </p> 090 * <ul> 091 * <li>{@link #getPath()}</li> 092 * <li>{@link MessageDigest}</li> 093 * </ul> 094 * 095 * @return a new instance. 096 * @throws NullPointerException if messageDigest is null. 097 * @throws IllegalStateException if the {@code origin} is {@code null}. 098 * @throws UnsupportedOperationException if the origin cannot be converted to an {@link InputStream}. 099 * @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}