001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.shiro.crypto.hash;
020    
021    import org.apache.shiro.codec.Base64;
022    import org.apache.shiro.codec.CodecException;
023    import org.apache.shiro.codec.Hex;
024    import org.apache.shiro.crypto.UnknownAlgorithmException;
025    import org.apache.shiro.util.ByteSource;
026    import org.apache.shiro.util.StringUtils;
027    
028    import java.security.MessageDigest;
029    import java.security.NoSuchAlgorithmException;
030    import java.util.Arrays;
031    
032    /**
033     * A {@code Hash} implementation that allows any {@link java.security.MessageDigest MessageDigest} algorithm name to
034     * be used.  This class is a less type-safe variant than the other {@code AbstractHash} subclasses
035     * (e.g. {@link Sha512Hash}, etc), but it does allow for any algorithm name to be specified in case the other subclass
036     * implementations do not represent an algorithm that you may want to use.
037     * <p/>
038     * As of Shiro 1.1, this class effectively replaces the (now-deprecated) {@link AbstractHash} class.  It subclasses
039     * {@code AbstractHash} only to retain backwards-compatibility.
040     *
041     * @since 1.1
042     */
043    public class SimpleHash extends AbstractHash {
044    
045        private static final int DEFAULT_ITERATIONS = 1;
046    
047        /**
048         * The {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash.
049         */
050        private final String algorithmName;
051    
052        /**
053         * The hashed data
054         */
055        private byte[] bytes;
056    
057        /**
058         * Supplied salt, if any.
059         */
060        private ByteSource salt;
061    
062        /**
063         * Number of hash iterations to perform.  Defaults to 1 in the constructor.
064         */
065        private int iterations;
066    
067        /**
068         * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead.
069         */
070        private transient String hexEncoded = null;
071    
072        /**
073         * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead.
074         */
075        private transient String base64Encoded = null;
076    
077        /**
078         * Creates an new instance with only its {@code algorithmName} set - no hashing is performed.
079         * <p/>
080         * Because all other constructors in this class hash the {@code source} constructor argument, this
081         * constructor is useful in scenarios when you have a byte array that you know is already hashed and
082         * just want to set the bytes in their raw form directly on an instance.  After using this constructor,
083         * you can then immediately call {@link #setBytes setBytes} to have a fully-initialized instance.
084         * <p/>
085         * <b>N.B.</b>The algorithm identified by the {@code algorithmName} parameter must be available on the JVM.  If it
086         * is not, a {@link UnknownAlgorithmException} will be thrown when the hash is performed (not at instantiation).
087         *
088         * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when
089         *                      performing the hash.
090         * @see UnknownAlgorithmException
091         */
092        public SimpleHash(String algorithmName) {
093            this.algorithmName = algorithmName;
094            this.iterations = DEFAULT_ITERATIONS;
095        }
096    
097        /**
098         * Creates an {@code algorithmName}-specific hash of the specified {@code source} with no {@code salt} using a
099         * single hash iteration.
100         * <p/>
101         * This is a convenience constructor that merely executes <code>this( algorithmName, source, null, 1);</code>.
102         * <p/>
103         * Please see the
104         * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) SimpleHashHash(algorithmName, Object,Object,int)}
105         * constructor for the types of Objects that may be passed into this constructor, as well as how to support further
106         * types.
107         *
108         * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when
109         *                      performing the hash.
110         * @param source        the object to be hashed.
111         * @throws org.apache.shiro.codec.CodecException
112         *                                   if the specified {@code source} cannot be converted into a byte array (byte[]).
113         * @throws UnknownAlgorithmException if the {@code algorithmName} is not available.
114         */
115        public SimpleHash(String algorithmName, Object source) throws CodecException, UnknownAlgorithmException {
116            //noinspection NullableProblems
117            this(algorithmName, source, null, DEFAULT_ITERATIONS);
118        }
119    
120        /**
121         * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given {@code salt}
122         * using a single hash iteration.
123         * <p/>
124         * It is a convenience constructor that merely executes <code>this( algorithmName, source, salt, 1);</code>.
125         * <p/>
126         * Please see the
127         * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) SimpleHashHash(algorithmName, Object,Object,int)}
128         * constructor for the types of Objects that may be passed into this constructor, as well as how to support further
129         * types.
130         *
131         * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when
132         *                      performing the hash.
133         * @param source        the source object to be hashed.
134         * @param salt          the salt to use for the hash
135         * @throws CodecException            if either constructor argument cannot be converted into a byte array.
136         * @throws UnknownAlgorithmException if the {@code algorithmName} is not available.
137         */
138        public SimpleHash(String algorithmName, Object source, Object salt) throws CodecException, UnknownAlgorithmException {
139            this(algorithmName, source, salt, DEFAULT_ITERATIONS);
140        }
141    
142        /**
143         * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given
144         * {@code salt} a total of {@code hashIterations} times.
145         * <p/>
146         * By default, this class only supports Object method arguments of
147         * type {@code byte[]}, {@code char[]}, {@link String}, {@link java.io.File File},
148         * {@link java.io.InputStream InputStream} or {@link org.apache.shiro.util.ByteSource ByteSource}.  If either
149         * argument is anything other than these types a {@link org.apache.shiro.codec.CodecException CodecException}
150         * will be thrown.
151         * <p/>
152         * If you want to be able to hash other object types, or use other salt types, you need to override the
153         * {@link #toBytes(Object) toBytes(Object)} method to support those specific types.  Your other option is to
154         * convert your arguments to one of the default supported types first before passing them in to this
155         * constructor}.
156         *
157         * @param algorithmName  the {@link java.security.MessageDigest MessageDigest} algorithm name to use when
158         *                       performing the hash.
159         * @param source         the source object to be hashed.
160         * @param salt           the salt to use for the hash
161         * @param hashIterations the number of times the {@code source} argument hashed for attack resiliency.
162         * @throws CodecException            if either Object constructor argument cannot be converted into a byte array.
163         * @throws UnknownAlgorithmException if the {@code algorithmName} is not available.
164         */
165        public SimpleHash(String algorithmName, Object source, Object salt, int hashIterations)
166                throws CodecException, UnknownAlgorithmException {
167            if (!StringUtils.hasText(algorithmName)) {
168                throw new NullPointerException("algorithmName argument cannot be null or empty.");
169            }
170            this.algorithmName = algorithmName;
171            this.iterations = Math.max(DEFAULT_ITERATIONS, hashIterations);
172            ByteSource saltBytes = null;
173            if (salt != null) {
174                saltBytes = convertSaltToBytes(salt);
175                this.salt = saltBytes;
176            }
177            ByteSource sourceBytes = convertSourceToBytes(source);
178            hash(sourceBytes, saltBytes, hashIterations);
179        }
180    
181        /**
182         * Acquires the specified {@code source} argument's bytes and returns them in the form of a {@code ByteSource} instance.
183         * <p/>
184         * This implementation merely delegates to the convenience {@link #toByteSource(Object)} method for generic
185         * conversion.  Can be overridden by subclasses for source-specific conversion.
186         *
187         * @param source the source object to be hashed.
188         * @return the source's bytes in the form of a {@code ByteSource} instance.
189         * @since 1.2
190         */
191        protected ByteSource convertSourceToBytes(Object source) {
192            return toByteSource(source);
193        }
194    
195        /**
196         * Acquires the specified {@code salt} argument's bytes and returns them in the form of a {@code ByteSource} instance.
197         * <p/>
198         * This implementation merely delegates to the convenience {@link #toByteSource(Object)} method for generic
199         * conversion.  Can be overridden by subclasses for salt-specific conversion.
200         *
201         * @param salt the salt to be use for the hash.
202         * @return the salt's bytes in the form of a {@code ByteSource} instance.
203         * @since 1.2
204         */
205        protected ByteSource convertSaltToBytes(Object salt) {
206            return toByteSource(salt);
207        }
208    
209        /**
210         * Converts a given object into a {@code ByteSource} instance.  Assumes the object can be converted to bytes.
211         *
212         * @param o the Object to convert into a {@code ByteSource} instance.
213         * @return the {@code ByteSource} representation of the specified object's bytes.
214         * @since 1.2
215         */
216        protected ByteSource toByteSource(Object o) {
217            if (o == null) {
218                return null;
219            }
220            if (o instanceof ByteSource) {
221                return (ByteSource) o;
222            }
223            byte[] bytes = toBytes(o);
224            return ByteSource.Util.bytes(bytes);
225        }
226    
227        private void hash(ByteSource source, ByteSource salt, int hashIterations) throws CodecException, UnknownAlgorithmException {
228            byte[] saltBytes = salt != null ? salt.getBytes() : null;
229            byte[] hashedBytes = hash(source.getBytes(), saltBytes, hashIterations);
230            setBytes(hashedBytes);
231        }
232    
233        /**
234         * Returns the {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash.
235         *
236         * @return the {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash.
237         */
238        public String getAlgorithmName() {
239            return this.algorithmName;
240        }
241    
242        public ByteSource getSalt() {
243            return this.salt;
244        }
245    
246        public int getIterations() {
247            return this.iterations;
248        }
249    
250        public byte[] getBytes() {
251            return this.bytes;
252        }
253    
254        /**
255         * Sets the raw bytes stored by this hash instance.
256         * <p/>
257         * The bytes are kept in raw form - they will not be hashed/changed.  This is primarily a utility method for
258         * constructing a Hash instance when the hashed value is already known.
259         *
260         * @param alreadyHashedBytes the raw already-hashed bytes to store in this instance.
261         */
262        public void setBytes(byte[] alreadyHashedBytes) {
263            this.bytes = alreadyHashedBytes;
264            this.hexEncoded = null;
265            this.base64Encoded = null;
266        }
267    
268        /**
269         * Sets the iterations used to previously compute AN ALREADY GENERATED HASH.
270         * <p/>
271         * This is provided <em>ONLY</em> to reconstitute an already-created Hash instance.  It should ONLY ever be
272         * invoked when re-constructing a hash instance from an already-hashed value.
273         *
274         * @param iterations the number of hash iterations used to previously create the hash/digest.
275         * @since 1.2
276         */
277        public void setIterations(int iterations) {
278            this.iterations = Math.max(DEFAULT_ITERATIONS, iterations);
279        }
280    
281        /**
282         * Sets the salt used to previously compute AN ALREADY GENERATED HASH.
283         * <p/>
284         * This is provided <em>ONLY</em> to reconstitute a Hash instance that has already been computed.  It should ONLY
285         * ever be invoked when re-constructing a hash instance from an already-hashed value.
286         *
287         * @param salt the salt used to previously create the hash/digest.
288         * @since 1.2
289         */
290        public void setSalt(ByteSource salt) {
291            this.salt = salt;
292        }
293    
294        /**
295         * Returns the JDK MessageDigest instance to use for executing the hash.
296         *
297         * @param algorithmName the algorithm to use for the hash, provided by subclasses.
298         * @return the MessageDigest object for the specified {@code algorithm}.
299         * @throws UnknownAlgorithmException if the specified algorithm name is not available.
300         */
301        protected MessageDigest getDigest(String algorithmName) throws UnknownAlgorithmException {
302            try {
303                return MessageDigest.getInstance(algorithmName);
304            } catch (NoSuchAlgorithmException e) {
305                String msg = "No native '" + algorithmName + "' MessageDigest instance available on the current JVM.";
306                throw new UnknownAlgorithmException(msg, e);
307            }
308        }
309    
310        /**
311         * Hashes the specified byte array without a salt for a single iteration.
312         *
313         * @param bytes the bytes to hash.
314         * @return the hashed bytes.
315         * @throws UnknownAlgorithmException if the configured {@link #getAlgorithmName() algorithmName} is not available.
316         */
317        protected byte[] hash(byte[] bytes) throws UnknownAlgorithmException {
318            return hash(bytes, null, DEFAULT_ITERATIONS);
319        }
320    
321        /**
322         * Hashes the specified byte array using the given {@code salt} for a single iteration.
323         *
324         * @param bytes the bytes to hash
325         * @param salt  the salt to use for the initial hash
326         * @return the hashed bytes
327         * @throws UnknownAlgorithmException if the configured {@link #getAlgorithmName() algorithmName} is not available.
328         */
329        protected byte[] hash(byte[] bytes, byte[] salt) throws UnknownAlgorithmException {
330            return hash(bytes, salt, DEFAULT_ITERATIONS);
331        }
332    
333        /**
334         * Hashes the specified byte array using the given {@code salt} for the specified number of iterations.
335         *
336         * @param bytes          the bytes to hash
337         * @param salt           the salt to use for the initial hash
338         * @param hashIterations the number of times the the {@code bytes} will be hashed (for attack resiliency).
339         * @return the hashed bytes.
340         * @throws UnknownAlgorithmException if the {@link #getAlgorithmName() algorithmName} is not available.
341         */
342        protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException {
343            MessageDigest digest = getDigest(getAlgorithmName());
344            if (salt != null) {
345                digest.reset();
346                digest.update(salt);
347            }
348            byte[] hashed = digest.digest(bytes);
349            int iterations = hashIterations - DEFAULT_ITERATIONS; //already hashed once above
350            //iterate remaining number:
351            for (int i = 0; i < iterations; i++) {
352                digest.reset();
353                hashed = digest.digest(hashed);
354            }
355            return hashed;
356        }
357    
358        public boolean isEmpty() {
359            return this.bytes == null || this.bytes.length == 0;
360        }
361    
362        /**
363         * Returns a hex-encoded string of the underlying {@link #getBytes byte array}.
364         * <p/>
365         * This implementation caches the resulting hex string so multiple calls to this method remain efficient.
366         * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the
367         * next time this method is called.
368         *
369         * @return a hex-encoded string of the underlying {@link #getBytes byte array}.
370         */
371        public String toHex() {
372            if (this.hexEncoded == null) {
373                this.hexEncoded = Hex.encodeToString(getBytes());
374            }
375            return this.hexEncoded;
376        }
377    
378        /**
379         * Returns a Base64-encoded string of the underlying {@link #getBytes byte array}.
380         * <p/>
381         * This implementation caches the resulting Base64 string so multiple calls to this method remain efficient.
382         * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the
383         * next time this method is called.
384         *
385         * @return a Base64-encoded string of the underlying {@link #getBytes byte array}.
386         */
387        public String toBase64() {
388            if (this.base64Encoded == null) {
389                //cache result in case this method is called multiple times.
390                this.base64Encoded = Base64.encodeToString(getBytes());
391            }
392            return this.base64Encoded;
393        }
394    
395        /**
396         * Simple implementation that merely returns {@link #toHex() toHex()}.
397         *
398         * @return the {@link #toHex() toHex()} value.
399         */
400        public String toString() {
401            return toHex();
402        }
403    
404        /**
405         * Returns {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to
406         * this Hash's byte array, {@code false} otherwise.
407         *
408         * @param o the object (Hash) to check for equality.
409         * @return {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to
410         *         this Hash's byte array, {@code false} otherwise.
411         */
412        public boolean equals(Object o) {
413            if (o instanceof Hash) {
414                Hash other = (Hash) o;
415                return Arrays.equals(getBytes(), other.getBytes());
416            }
417            return false;
418        }
419    
420        /**
421         * Simply returns toHex().hashCode();
422         *
423         * @return toHex().hashCode()
424         */
425        public int hashCode() {
426            if (this.bytes == null || this.bytes.length == 0) {
427                return 0;
428            }
429            return Arrays.hashCode(this.bytes);
430        }
431    }