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 */
019package org.apache.shiro.crypto.hash;
020
021import org.apache.shiro.codec.Base64;
022import org.apache.shiro.codec.CodecException;
023import org.apache.shiro.codec.Hex;
024import org.apache.shiro.crypto.UnknownAlgorithmException;
025import org.apache.shiro.util.ByteSource;
026import org.apache.shiro.util.StringUtils;
027
028import java.security.MessageDigest;
029import java.security.NoSuchAlgorithmException;
030import 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 */
043public 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}