View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.shiro.crypto.hash;
20  
21  import org.apache.shiro.crypto.UnknownAlgorithmException;
22  import org.apache.shiro.lang.codec.Base64;
23  import org.apache.shiro.lang.codec.CodecException;
24  import org.apache.shiro.lang.codec.CodecSupport;
25  import org.apache.shiro.lang.codec.Hex;
26  
27  import java.io.Serializable;
28  import java.security.MessageDigest;
29  import java.security.NoSuchAlgorithmException;
30  import java.util.Arrays;
31  
32  /**
33   * Provides a base for all Shiro Hash algorithms with support for salts and multiple hash iterations.
34   * <p/>
35   * Read
36   * <a href="http://www.owasp.org/index.php/Hashing_Java" target="blank">http://www.owasp.org/index.php/Hashing_Java</a>
37   * for a good article on the benefits of hashing, including what a 'salt' is as well as why it and multiple hash
38   * iterations can be useful.
39   * <p/>
40   * This class and its subclasses support hashing with additional capabilities of salting and multiple iterations via
41   * overloaded constructors.
42   *
43   * @since 0.9
44   * @deprecated in Shiro 1.1 in favor of using the concrete {@link SimpleHash} implementation directly.
45   */
46  @Deprecated
47  public abstract class AbstractHash extends CodecSupport implements Hash, Serializable {
48  
49      private static final long serialVersionUID = -4723044219611288405L;
50      /**
51       * The hashed data
52       */
53      private byte[] bytes;
54  
55      /**
56       * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead.
57       */
58      private transient String hexEncoded;
59      /**
60       * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead.
61       */
62      private transient String base64Encoded;
63  
64      /**
65       * Creates an new instance without any of its properties set (no hashing is performed).
66       * <p/>
67       * Because all constructors in this class (except this one) hash the {@code source} constructor argument, this
68       * default, no-arg constructor is useful in scenarios when you have a byte array that you know is already hashed and
69       * just want to set the bytes in their raw form directly on an instance.  After instantiating the instance with
70       * this default, no-arg constructor, you can then immediately call {@link #setBytes setBytes} to have a
71       * fully-initialized instance.
72       */
73      public AbstractHash() {
74      }
75  
76      /**
77       * Creates a hash of the specified {@code source} with no {@code salt} using a single hash iteration.
78       * <p/>
79       * It is a convenience constructor that merely executes <code>this( source, null, 1);</code>.
80       * <p/>
81       * Please see the
82       * {@link #AbstractHash(Object source, Object salt, int numIterations) AbstractHash(Object,Object,int)}
83       * constructor for the types of Objects that may be passed into this constructor, as well as how to support further
84       * types.
85       *
86       * @param source the object to be hashed.
87       * @throws CodecException if the specified {@code source} cannot be converted into a byte array (byte[]).
88       */
89      public AbstractHash(Object source) throws CodecException {
90          this(source, null, 1);
91      }
92  
93      /**
94       * Creates a hash of the specified {@code source} using the given {@code salt} using a single hash iteration.
95       * <p/>
96       * It is a convenience constructor that merely executes <code>this( source, salt, 1);</code>.
97       * <p/>
98       * Please see the
99       * {@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 }