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;
026
027import java.io.Serializable;
028import java.security.MessageDigest;
029import java.security.NoSuchAlgorithmException;
030import java.util.Arrays;
031
032/**
033 * Provides a base for all Shiro Hash algorithms with support for salts and multiple hash iterations.
034 * <p/>
035 * Read
036 * <a href="http://www.owasp.org/index.php/Hashing_Java" target="blank">http://www.owasp.org/index.php/Hashing_Java</a>
037 * for a good article on the benefits of hashing, including what a 'salt' is as well as why it and multiple hash
038 * iterations can be useful.
039 * <p/>
040 * This class and its subclasses support hashing with additional capabilities of salting and multiple iterations via
041 * overloaded constructors.
042 *
043 * @since 0.9
044 * @deprecated in Shiro 1.1 in favor of using the concrete {@link SimpleHash} implementation directly.
045 */
046@Deprecated
047public abstract class AbstractHash extends CodecSupport implements Hash, Serializable {
048
049    private static final long serialVersionUID = -4723044219611288405L;
050    /**
051     * The hashed data
052     */
053    private byte[] bytes;
054
055    /**
056     * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead.
057     */
058    private transient String hexEncoded;
059    /**
060     * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead.
061     */
062    private transient String base64Encoded;
063
064    /**
065     * Creates an new instance without any of its properties set (no hashing is performed).
066     * <p/>
067     * Because all constructors in this class (except this one) hash the {@code source} constructor argument, this
068     * default, no-arg constructor is useful in scenarios when you have a byte array that you know is already hashed and
069     * just want to set the bytes in their raw form directly on an instance.  After instantiating the instance with
070     * this default, no-arg constructor, you can then immediately call {@link #setBytes setBytes} to have a
071     * fully-initialized instance.
072     */
073    public AbstractHash() {
074    }
075
076    /**
077     * Creates a hash of the specified {@code source} with no {@code salt} using a single hash iteration.
078     * <p/>
079     * It is a convenience constructor that merely executes <code>this( source, null, 1);</code>.
080     * <p/>
081     * Please see the
082     * {@link #AbstractHash(Object source, Object salt, int numIterations) AbstractHash(Object,Object,int)}
083     * constructor for the types of Objects that may be passed into this constructor, as well as how to support further
084     * types.
085     *
086     * @param source the object to be hashed.
087     * @throws CodecException if the specified {@code source} cannot be converted into a byte array (byte[]).
088     */
089    public AbstractHash(Object source) throws CodecException {
090        this(source, null, 1);
091    }
092
093    /**
094     * Creates a hash of the specified {@code source} using the given {@code salt} using a single hash iteration.
095     * <p/>
096     * It is a convenience constructor that merely executes <code>this( source, salt, 1);</code>.
097     * <p/>
098     * Please see the
099     * {@link #AbstractHash(Object source, Object salt, int numIterations) AbstractHash(Object,Object,int)}
100     * constructor for the types of Objects that may be passed into this constructor, as well as how to support further
101     * types.
102     *
103     * @param source the source object to be hashed.
104     * @param salt   the salt to use for the hash
105     * @throws CodecException if either constructor argument cannot be converted into a byte array.
106     */
107    public AbstractHash(Object source, Object salt) throws CodecException {
108        this(source, salt, 1);
109    }
110
111    /**
112     * Creates a hash of the specified {@code source} using the given {@code salt} a total of
113     * {@code hashIterations} times.
114     * <p/>
115     * By default, this class only supports Object method arguments of
116     * type {@code byte[]}, {@code char[]}, {@link String}, {@link java.io.File File}, or
117     * {@link java.io.InputStream InputStream}.  If either argument is anything other than these
118     * types a {@link org.apache.shiro.lang.codec.CodecException CodecException} will be thrown.
119     * <p/>
120     * If you want to be able to hash other object types, or use other salt types, you need to override the
121     * {@link #toBytes(Object) toBytes(Object)} method to support those specific types.  Your other option is to
122     * convert your arguments to one of the default three supported types first before passing them in to this
123     * constructor}.
124     *
125     * @param source         the source object to be hashed.
126     * @param salt           the salt to use for the hash
127     * @param hashIterations the number of times the {@code source} argument hashed for attack resiliency.
128     * @throws CodecException if either Object constructor argument cannot be converted into a byte array.
129     */
130    public AbstractHash(Object source, Object salt, int hashIterations) throws CodecException {
131        byte[] sourceBytes = toBytes(source);
132        byte[] saltBytes = null;
133        if (salt != null) {
134            saltBytes = toBytes(salt);
135        }
136        byte[] hashedBytes = hash(sourceBytes, saltBytes, hashIterations);
137        setBytes(hashedBytes);
138    }
139
140    /**
141     * Implemented by subclasses, this specifies the {@link MessageDigest MessageDigest} algorithm name
142     * to use when performing the hash.
143     *
144     * @return the {@link MessageDigest MessageDigest} algorithm name to use when performing the hash.
145     */
146    @Override
147    public abstract String getAlgorithmName();
148
149    @Override
150    public byte[] getBytes() {
151        return this.bytes;
152    }
153
154    /**
155     * Sets the raw bytes stored by this hash instance.
156     * <p/>
157     * The bytes are kept in raw form - they will not be hashed/changed.  This is primarily a utility method for
158     * constructing a Hash instance when the hashed value is already known.
159     *
160     * @param alreadyHashedBytes the raw already-hashed bytes to store in this instance.
161     */
162    public void setBytes(byte[] alreadyHashedBytes) {
163        this.bytes = alreadyHashedBytes;
164        this.hexEncoded = null;
165        this.base64Encoded = null;
166    }
167
168    /**
169     * Returns the JDK MessageDigest instance to use for executing the hash.
170     *
171     * @param algorithmName the algorithm to use for the hash, provided by subclasses.
172     * @return the MessageDigest object for the specified {@code algorithm}.
173     * @throws UnknownAlgorithmException if the specified algorithm name is not available.
174     */
175    protected MessageDigest getDigest(String algorithmName) throws UnknownAlgorithmException {
176        try {
177            return MessageDigest.getInstance(algorithmName);
178        } catch (NoSuchAlgorithmException e) {
179            String msg = "No native '" + algorithmName + "' MessageDigest instance available on the current JVM.";
180            throw new UnknownAlgorithmException(msg, e);
181        }
182    }
183
184    /**
185     * Hashes the specified byte array without a salt for a single iteration.
186     *
187     * @param bytes the bytes to hash.
188     * @return the hashed bytes.
189     */
190    protected byte[] hash(byte[] bytes) {
191        return hash(bytes, null, 1);
192    }
193
194    /**
195     * Hashes the specified byte array using the given {@code salt} for a single iteration.
196     *
197     * @param bytes the bytes to hash
198     * @param salt  the salt to use for the initial hash
199     * @return the hashed bytes
200     */
201    protected byte[] hash(byte[] bytes, byte[] salt) {
202        return hash(bytes, salt, 1);
203    }
204
205    /**
206     * Hashes the specified byte array using the given {@code salt} for the specified number of iterations.
207     *
208     * @param bytes          the bytes to hash
209     * @param salt           the salt to use for the initial hash
210     * @param hashIterations the number of times the the {@code bytes} will be hashed (for attack resiliency).
211     * @return the hashed bytes.
212     * @throws UnknownAlgorithmException if the {@link #getAlgorithmName() algorithmName} is not available.
213     */
214    protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException {
215        MessageDigest digest = getDigest(getAlgorithmName());
216        if (salt != null) {
217            digest.reset();
218            digest.update(salt);
219        }
220        byte[] hashed = digest.digest(bytes);
221        //already hashed once above
222        int iterations = hashIterations - 1;
223        //iterate remaining number:
224        for (int i = 0; i < iterations; i++) {
225            digest.reset();
226            hashed = digest.digest(hashed);
227        }
228        return hashed;
229    }
230
231    /**
232     * Returns a hex-encoded string of the underlying {@link #getBytes byte array}.
233     * <p/>
234     * This implementation caches the resulting hex string so multiple calls to this method remain efficient.
235     * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the
236     * next time this method is called.
237     *
238     * @return a hex-encoded string of the underlying {@link #getBytes byte array}.
239     */
240    @Override
241    public String toHex() {
242        if (this.hexEncoded == null) {
243            this.hexEncoded = Hex.encodeToString(getBytes());
244        }
245        return this.hexEncoded;
246    }
247
248    /**
249     * Returns a Base64-encoded string of the underlying {@link #getBytes byte array}.
250     * <p/>
251     * This implementation caches the resulting Base64 string so multiple calls to this method remain efficient.
252     * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the
253     * next time this method is called.
254     *
255     * @return a Base64-encoded string of the underlying {@link #getBytes byte array}.
256     */
257    @Override
258    public String toBase64() {
259        if (this.base64Encoded == null) {
260            //cache result in case this method is called multiple times.
261            this.base64Encoded = Base64.encodeToString(getBytes());
262        }
263        return this.base64Encoded;
264    }
265
266    /**
267     * Simple implementation that merely returns {@link #toHex() toHex()}.
268     *
269     * @return the {@link #toHex() toHex()} value.
270     */
271    @Override
272    public String toString() {
273        return toHex();
274    }
275
276    /**
277     * Returns {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to
278     * this Hash's byte array, {@code false} otherwise.
279     *
280     * @param o the object (Hash) to check for equality.
281     * @return {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to
282     * this Hash's byte array, {@code false} otherwise.
283     */
284    @Override
285    public boolean equals(Object o) {
286        if (o instanceof Hash) {
287            Hash other = (Hash) o;
288            return MessageDigest.isEqual(getBytes(), other.getBytes());
289        }
290        return false;
291    }
292
293    /**
294     * Simply returns toHex().hashCode();
295     *
296     * @return toHex().hashCode()
297     */
298    @Override
299    public int hashCode() {
300        if (this.bytes == null || this.bytes.length == 0) {
301            return 0;
302        }
303        return Arrays.hashCode(this.bytes);
304    }
305
306}