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, see {@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 * @deprecated Use {@link MessageDigestInputStream}. 042 */ 043@Deprecated 044public class MessageDigestCalculatingInputStream extends ObservableInputStream { 045 046 /** 047 * Builds a new {@link MessageDigestCalculatingInputStream} instance. 048 * <p> 049 * For example: 050 * </p> 051 * <pre>{@code 052 * MessageDigestCalculatingInputStream s = MessageDigestCalculatingInputStream.builder() 053 * .setPath(path) 054 * .setMessageDigest("SHA-512") 055 * .get();} 056 * </pre> 057 * 058 * @since 2.12.0 059 */ 060 public static class Builder extends AbstractStreamBuilder<MessageDigestCalculatingInputStream, Builder> { 061 062 private MessageDigest messageDigest; 063 064 /** 065 * Constructs a new Builder. 066 */ 067 public Builder() { 068 try { 069 this.messageDigest = getDefaultMessageDigest(); 070 } catch (final NoSuchAlgorithmException e) { 071 // Should not happen. 072 throw new IllegalStateException(e); 073 } 074 } 075 076 /** 077 * Constructs a new instance. 078 * <p> 079 * This builder use the aspects InputStream, OpenOption[], and MessageDigest. 080 * </p> 081 * <p> 082 * You must provide an origin that can be converted to an InputStream by this builder, otherwise, this call will throw an 083 * {@link UnsupportedOperationException}. 084 * </p> 085 * 086 * @return a new instance. 087 * @throws UnsupportedOperationException if the origin cannot provide an InputStream. 088 * @see #getInputStream() 089 */ 090 @SuppressWarnings("resource") 091 @Override 092 public MessageDigestCalculatingInputStream get() throws IOException { 093 return new MessageDigestCalculatingInputStream(getInputStream(), messageDigest); 094 } 095 096 /** 097 * Sets the message digest. 098 * <p> 099 * 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}