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; 024 025import org.apache.commons.io.build.AbstractStreamBuilder; 026 027/** 028 * This class is an example for using an {@link ObservableInputStream}. It creates its own {@link org.apache.commons.io.input.ObservableInputStream.Observer}, 029 * which calculates a checksum using a MessageDigest, for example an MD5 sum. 030 * <p> 031 * See the MessageDigest section in the <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java 032 * Cryptography Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names. 033 * </p> 034 * <p> 035 * <em>Note</em>: Neither {@link ObservableInputStream}, nor {@link MessageDigest}, are thread safe. So is {@link MessageDigestCalculatingInputStream}. 036 * </p> 037 * <p> 038 * TODO Rename to MessageDigestInputStream in 3.0. 039 * </p> 040 */ 041public class MessageDigestCalculatingInputStream extends ObservableInputStream { 042 043 /** 044 * Builds a new {@link MessageDigestCalculatingInputStream} instance. 045 * <p> 046 * For example: 047 * </p> 048 * <pre>{@code 049 * MessageDigestCalculatingInputStream s = MessageDigestCalculatingInputStream.builder() 050 * .setPath(path) 051 * .setMessageDigest("SHA-512") 052 * .get()} 053 * </pre> 054 * <p> 055 * @since 2.12.0 056 */ 057 public static class Builder extends AbstractStreamBuilder<MessageDigestCalculatingInputStream, Builder> { 058 059 private MessageDigest messageDigest; 060 061 public Builder() { 062 try { 063 this.messageDigest = getDefaultMessageDigest(); 064 } catch (final NoSuchAlgorithmException e) { 065 // Should not happen. 066 throw new IllegalStateException(e); 067 } 068 } 069 070 /** 071 * Constructs a new instance. 072 * 073 * @throws UnsupportedOperationException if the origin cannot be converted to an InputStream. 074 */ 075 @SuppressWarnings("resource") 076 @Override 077 public MessageDigestCalculatingInputStream get() throws IOException { 078 return new MessageDigestCalculatingInputStream(getOrigin().getInputStream(), messageDigest); 079 } 080 081 /** 082 * Sets the message digest. 083 * 084 * @param messageDigest the message digest. 085 */ 086 public void setMessageDigest(final MessageDigest messageDigest) { 087 this.messageDigest = messageDigest; 088 } 089 090 /** 091 * Sets the name of the name of the message digest algorithm. 092 * 093 * @param algorithm the name of the algorithm. See the MessageDigest section in the 094 * <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java Cryptography 095 * Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names. 096 * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified algorithm. 097 */ 098 public void setMessageDigest(final String algorithm) throws NoSuchAlgorithmException { 099 this.messageDigest = MessageDigest.getInstance(algorithm); 100 } 101 102 } 103 104 /** 105 * Maintains the message digest. 106 */ 107 public static class MessageDigestMaintainingObserver extends Observer { 108 private final MessageDigest messageDigest; 109 110 /** 111 * Creates an MessageDigestMaintainingObserver for the given MessageDigest. 112 * 113 * @param messageDigest the message digest to use 114 */ 115 public MessageDigestMaintainingObserver(final MessageDigest messageDigest) { 116 this.messageDigest = messageDigest; 117 } 118 119 @Override 120 public void data(final byte[] input, final int offset, final int length) throws IOException { 121 messageDigest.update(input, offset, length); 122 } 123 124 @Override 125 public void data(final int input) throws IOException { 126 messageDigest.update((byte) input); 127 } 128 } 129 130 /** 131 * The default message digest algorithm. 132 * <p> 133 * The MD5 cryptographic algorithm is weak and should not be used. 134 * </p> 135 */ 136 private static final String DEFAULT_ALGORITHM = "MD5"; 137 138 /** 139 * Constructs a new {@link Builder}. 140 * 141 * @return a new {@link Builder}. 142 * @since 2.12.0 143 */ 144 public static Builder builder() { 145 return new Builder(); 146 } 147 148 /** 149 * Gets a MessageDigest object that implements the default digest algorithm. 150 * 151 * @return a Message Digest object that implements the default algorithm. 152 * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation. 153 * @see Provider 154 */ 155 static MessageDigest getDefaultMessageDigest() throws NoSuchAlgorithmException { 156 return MessageDigest.getInstance(DEFAULT_ALGORITHM); 157 } 158 159 private final MessageDigest messageDigest; 160 161 /** 162 * Creates a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the "MD5" algorithm. 163 * <p> 164 * The MD5 algorithm is weak and should not be used. 165 * </p> 166 * 167 * @param inputStream the stream to calculate the message digest for 168 * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified algorithm. 169 * @deprecated Use {@link #builder()}. 170 */ 171 @Deprecated 172 public MessageDigestCalculatingInputStream(final InputStream inputStream) throws NoSuchAlgorithmException { 173 this(inputStream, getDefaultMessageDigest()); 174 } 175 176 /** 177 * Creates a new instance, which calculates a signature on the given stream, using the given {@link MessageDigest}. 178 * 179 * @param inputStream the stream to calculate the message digest for 180 * @param messageDigest the message digest to use 181 * @deprecated Use {@link #builder()}. 182 */ 183 @Deprecated 184 public MessageDigestCalculatingInputStream(final InputStream inputStream, final MessageDigest messageDigest) { 185 super(inputStream, new MessageDigestMaintainingObserver(messageDigest)); 186 this.messageDigest = messageDigest; 187 } 188 189 /** 190 * Creates a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the given algorithm. 191 * 192 * @param inputStream the stream to calculate the message digest for 193 * @param algorithm the name of the algorithm requested. See the MessageDigest section in the 194 * <a href= "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest"> Java Cryptography 195 * Architecture Standard Algorithm Name Documentation</a> for information about standard algorithm names. 196 * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified algorithm. 197 * @deprecated Use {@link #builder()}. 198 */ 199 @Deprecated 200 public MessageDigestCalculatingInputStream(final InputStream inputStream, final String algorithm) throws NoSuchAlgorithmException { 201 this(inputStream, MessageDigest.getInstance(algorithm)); 202 } 203 204 /** 205 * Gets the {@link MessageDigest}, which is being used for generating the checksum. 206 * <p> 207 * <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 208 * data has been read, if that is what you want. The easiest way to do so is by invoking {@link #consume()}. 209 * </p> 210 * 211 * @return the message digest used 212 */ 213 public MessageDigest getMessageDigest() { 214 return messageDigest; 215 } 216}