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 }