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 import org.apache.shiro.lang.util.ByteSource;
27 import org.apache.shiro.lang.util.SimpleByteSource;
28 import org.apache.shiro.lang.util.StringUtils;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 import java.io.Serializable;
33 import java.security.MessageDigest;
34 import java.security.NoSuchAlgorithmException;
35 import java.util.Arrays;
36
37 import static java.util.Objects.requireNonNull;
38
39 /**
40 * A {@code Hash} implementation that allows any {@link java.security.MessageDigest MessageDigest} algorithm name to
41 * be used. This class is a less type-safe variant than the other {@code AbstractHash} subclasses
42 * (e.g. {@link Sha512Hash}, etc.), but it does allow for any algorithm name to be specified in case the other subclass
43 * implementations do not represent an algorithm that you may want to use.
44 * <p/>
45 * As of Shiro 1.1, this class effectively replaces the (now-deprecated) {@code AbstractHash} class. It subclasses
46 * {@code AbstractHash} only to retain backwards-compatibility.
47 *
48 * @since 1.1
49 */
50 public class SimpleHash extends CodecSupport implements Hash, Serializable {
51
52 private static final int DEFAULT_ITERATIONS = 1;
53 private static final long serialVersionUID = -6689895264902387303L;
54
55 private static final Logger LOG = LoggerFactory.getLogger(SimpleHash.class);
56
57 /**
58 * The {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash.
59 */
60 private final String algorithmName;
61
62 /**
63 * The hashed data
64 */
65 private byte[] bytes;
66
67 /**
68 * Supplied salt, if any.
69 */
70 private ByteSource salt;
71
72 /**
73 * Number of hash iterations to perform. Defaults to 1 in the constructor.
74 */
75 private int iterations;
76
77 /**
78 * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead.
79 */
80 private transient String hexEncoded;
81
82 /**
83 * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead.
84 */
85 private transient String base64Encoded;
86
87 /**
88 * Creates an new instance with only its {@code algorithmName} set - no hashing is performed.
89 * <p/>
90 * Because all other constructors in this class hash the {@code source} constructor argument, this
91 * constructor is useful in scenarios when you have a byte array that you know is already hashed and
92 * just want to set the bytes in their raw form directly on an instance. After using this constructor,
93 * you can then immediately call {@link #setBytes setBytes} to have a fully-initialized instance.
94 * <p/>
95 * <b>N.B.</b>The algorithm identified by the {@code algorithmName} parameter must be available on the JVM. If it
96 * is not, a {@link UnknownAlgorithmException} will be thrown when the hash is performed (not at instantiation).
97 *
98 * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when
99 * 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 }