001    /*
002     * JBoss DNA (http://www.jboss.org/dna)
003     * See the COPYRIGHT.txt file distributed with this work for information
004     * regarding copyright ownership.  Some portions may be licensed
005     * to Red Hat, Inc. under one or more contributor license agreements.
006     * See the AUTHORS.txt file in the distribution for a full listing of 
007     * individual contributors. 
008     *
009     * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010     * is licensed to you under the terms of the GNU Lesser General Public License as
011     * published by the Free Software Foundation; either version 2.1 of
012     * the License, or (at your option) any later version.
013     *
014     * JBoss DNA is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     * Lesser General Public License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this software; if not, write to the Free
021     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023     */
024    package org.jboss.dna.common.util;
025    
026    import java.io.BufferedInputStream;
027    import java.io.File;
028    import java.io.FileInputStream;
029    import java.io.IOException;
030    import java.io.InputStream;
031    import java.security.MessageDigest;
032    import java.security.NoSuchAlgorithmException;
033    
034    /**
035     * @author Randall Hauch
036     */
037    public class SecureHash {
038    
039        /**
040         * Commonly-used hashing algorithms.
041         */
042        public enum Algorithm {
043            MD2("MD2", "The MD2 message digest algorithm as defined in RFC 1319"),
044            MD5("MD5", "The MD5 message digest algorithm as defined in RFC 1321"),
045            SHA_1("SHA-1", "The Secure Hash Algorithm, as defined in Secure Hash Standard, NIST FIPS 180-1"),
046            SHA_256(
047                    "SHA-256",
048                    "New hash algorithms for which the draft Federal Information Processing Standard 180-2, "
049                    + "Secure Hash Standard (SHS) is now available.  SHA-256 is a 256-bit hash function intended to provide 128 bits of "
050                    + "security against collision attacks."),
051            SHA_384(
052                    "SHA-384",
053                    "New hash algorithms for which the draft Federal Information Processing Standard 180-2, "
054                    + "Secure Hash Standard (SHS) is now available.  A 384-bit hash may be obtained by truncating the SHA-512 output."),
055            SHA_512(
056                    "SHA-512",
057                    "New hash algorithms for which the draft Federal Information Processing Standard 180-2, "
058                    + "Secure Hash Standard (SHS) is now available.  SHA-512 is a 512-bit hash function intended to provide 256 bits of security.");
059            private String name;
060            private String description;
061    
062            private Algorithm( String name,
063                               String description ) {
064                this.name = name;
065                this.description = description;
066            }
067    
068            public String digestName() {
069                return this.name;
070            }
071    
072            public String description() {
073                return this.description;
074            }
075    
076            @Override
077            public String toString() {
078                return digestName();
079            }
080        }
081    
082        /**
083         * Get the hash of the supplied content, using the supplied digest algorithm.
084         * 
085         * @param algorithm the hashing function algorithm that should be used
086         * @param content the content to be hashed; may not be null
087         * @return the hash of the contents as a byte array
088         * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
089         * @throws IllegalArgumentException if the algorithm is null
090         */
091        public static byte[] getHash( Algorithm algorithm,
092                                      byte[] content ) throws NoSuchAlgorithmException {
093            CheckArg.isNotNull(algorithm, "algorithm");
094            return getHash(algorithm.digestName(), content);
095        }
096    
097        /**
098         * Get the hash of the supplied content, using the supplied digest algorithm.
099         * 
100         * @param algorithm the hashing function algorithm that should be used
101         * @param file the file containing the content to be hashed; may not be null
102         * @return the hash of the contents as a byte array
103         * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
104         * @throws IllegalArgumentException if the algorithm is null
105         * @throws IOException if there is an error reading the file
106         */
107        public static byte[] getHash( Algorithm algorithm,
108                                      File file ) throws NoSuchAlgorithmException, IOException {
109            CheckArg.isNotNull(algorithm, "algorithm");
110            return getHash(algorithm.digestName(), file);
111        }
112    
113        /**
114         * Get the hash of the supplied content, using the supplied digest algorithm.
115         * 
116         * @param algorithm the hashing function algorithm that should be used
117         * @param stream the stream containing the content to be hashed; may not be null
118         * @return the hash of the contents as a byte array
119         * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
120         * @throws IllegalArgumentException if the algorithm is null
121         * @throws IOException if there is an error reading the stream
122         */
123        public static byte[] getHash( Algorithm algorithm,
124                                      InputStream stream ) throws NoSuchAlgorithmException, IOException {
125            CheckArg.isNotNull(algorithm, "algorithm");
126            return getHash(algorithm.digestName(), stream);
127        }
128    
129        /**
130         * Get the hash of the supplied content, using the digest identified by the supplied name.
131         * 
132         * @param digestName the name of the hashing function (or {@link MessageDigest message digest}) that should be used
133         * @param content the content to be hashed; may not be null
134         * @return the hash of the contents as a byte array
135         * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
136         */
137        public static byte[] getHash( String digestName,
138                                      byte[] content ) throws NoSuchAlgorithmException {
139            MessageDigest digest = MessageDigest.getInstance(digestName);
140            assert digest != null;
141            return digest.digest(content);
142        }
143    
144        /**
145         * Get the hash of the supplied content, using the digest identified by the supplied name.
146         * 
147         * @param digestName the name of the hashing function (or {@link MessageDigest message digest}) that should be used
148         * @param file the file whose content is to be hashed; may not be null
149         * @return the hash of the contents as a byte array
150         * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
151         * @throws IOException if there is an error reading the file
152         */
153        public static byte[] getHash( String digestName,
154                                      File file ) throws NoSuchAlgorithmException, IOException {
155            CheckArg.isNotNull(file, "file");
156            MessageDigest digest = MessageDigest.getInstance(digestName);
157            assert digest != null;
158            InputStream in = new BufferedInputStream(new FileInputStream(file));
159            boolean error = false;
160            try {
161                int bufSize = 1024;
162                byte[] buffer = new byte[bufSize];
163                int n = in.read(buffer, 0, bufSize);
164                int count = 0;
165                while (n != -1) {
166                    count += n;
167                    digest.update(buffer, 0, n);
168                    n = in.read(buffer, 0, bufSize);
169                }
170            } catch (IOException e) {
171                error = true;
172                throw e;
173            } finally {
174                try {
175                    in.close();
176                } catch (IOException e) {
177                    if (!error) throw e;
178                }
179            }
180            return digest.digest();
181        }
182    
183        /**
184         * Get the hash of the supplied content, using the digest identified by the supplied name. Note that this method never closes
185         * the supplied stream.
186         * 
187         * @param digestName the name of the hashing function (or {@link MessageDigest message digest}) that should be used
188         * @param stream the stream containing the content to be hashed; may not be null
189         * @return the hash of the contents as a byte array
190         * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
191         * @throws IOException if there is an error reading the stream
192         */
193        public static byte[] getHash( String digestName,
194                                      InputStream stream ) throws NoSuchAlgorithmException, IOException {
195            CheckArg.isNotNull(stream, "stream");
196            MessageDigest digest = MessageDigest.getInstance(digestName);
197            assert digest != null;
198            int bufSize = 1024;
199            byte[] buffer = new byte[bufSize];
200            int n = stream.read(buffer, 0, bufSize);
201            int count = 0;
202            while (n != -1) {
203                count += n;
204                digest.update(buffer, 0, n);
205                n = stream.read(buffer, 0, bufSize);
206            }
207            return digest.digest();
208        }
209    }