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