Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
SimpleHash |
|
| 1.84;1.84 |
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.codec.Base64; | |
22 | import org.apache.shiro.codec.CodecException; | |
23 | import org.apache.shiro.codec.Hex; | |
24 | import org.apache.shiro.crypto.UnknownAlgorithmException; | |
25 | import org.apache.shiro.util.ByteSource; | |
26 | import org.apache.shiro.util.StringUtils; | |
27 | ||
28 | import java.security.MessageDigest; | |
29 | import java.security.NoSuchAlgorithmException; | |
30 | import java.util.Arrays; | |
31 | ||
32 | /** | |
33 | * A {@code Hash} implementation that allows any {@link java.security.MessageDigest MessageDigest} algorithm name to | |
34 | * be used. This class is a less type-safe variant than the other {@code AbstractHash} subclasses | |
35 | * (e.g. {@link Sha512Hash}, etc), but it does allow for any algorithm name to be specified in case the other subclass | |
36 | * implementations do not represent an algorithm that you may want to use. | |
37 | * <p/> | |
38 | * As of Shiro 1.1, this class effectively replaces the (now-deprecated) {@link AbstractHash} class. It subclasses | |
39 | * {@code AbstractHash} only to retain backwards-compatibility. | |
40 | * | |
41 | * @since 1.1 | |
42 | */ | |
43 | public class SimpleHash extends AbstractHash { | |
44 | ||
45 | private static final int DEFAULT_ITERATIONS = 1; | |
46 | ||
47 | /** | |
48 | * The {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. | |
49 | */ | |
50 | private final String algorithmName; | |
51 | ||
52 | /** | |
53 | * The hashed data | |
54 | */ | |
55 | private byte[] bytes; | |
56 | ||
57 | /** | |
58 | * Supplied salt, if any. | |
59 | */ | |
60 | private ByteSource salt; | |
61 | ||
62 | /** | |
63 | * Number of hash iterations to perform. Defaults to 1 in the constructor. | |
64 | */ | |
65 | private int iterations; | |
66 | ||
67 | /** | |
68 | * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead. | |
69 | */ | |
70 | 135 | private transient String hexEncoded = null; |
71 | ||
72 | /** | |
73 | * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead. | |
74 | */ | |
75 | 135 | private transient String base64Encoded = null; |
76 | ||
77 | /** | |
78 | * Creates an new instance with only its {@code algorithmName} set - no hashing is performed. | |
79 | * <p/> | |
80 | * Because all other constructors in this class hash the {@code source} constructor argument, this | |
81 | * constructor is useful in scenarios when you have a byte array that you know is already hashed and | |
82 | * just want to set the bytes in their raw form directly on an instance. After using this constructor, | |
83 | * you can then immediately call {@link #setBytes setBytes} to have a fully-initialized instance. | |
84 | * <p/> | |
85 | * <b>N.B.</b>The algorithm identified by the {@code algorithmName} parameter must be available on the JVM. If it | |
86 | * is not, a {@link UnknownAlgorithmException} will be thrown when the hash is performed (not at instantiation). | |
87 | * | |
88 | * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when | |
89 | * performing the hash. | |
90 | * @see UnknownAlgorithmException | |
91 | */ | |
92 | 54 | public SimpleHash(String algorithmName) { |
93 | 54 | this.algorithmName = algorithmName; |
94 | 54 | this.iterations = DEFAULT_ITERATIONS; |
95 | 54 | } |
96 | ||
97 | /** | |
98 | * Creates an {@code algorithmName}-specific hash of the specified {@code source} with no {@code salt} using a | |
99 | * single hash iteration. | |
100 | * <p/> | |
101 | * This is a convenience constructor that merely executes <code>this( algorithmName, source, null, 1);</code>. | |
102 | * <p/> | |
103 | * Please see the | |
104 | * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) SimpleHashHash(algorithmName, Object,Object,int)} | |
105 | * constructor for the types of Objects that may be passed into this constructor, as well as how to support further | |
106 | * types. | |
107 | * | |
108 | * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when | |
109 | * performing the hash. | |
110 | * @param source the object to be hashed. | |
111 | * @throws org.apache.shiro.codec.CodecException | |
112 | * if the specified {@code source} cannot be converted into a byte array (byte[]). | |
113 | * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. | |
114 | */ | |
115 | public SimpleHash(String algorithmName, Object source) throws CodecException, UnknownAlgorithmException { | |
116 | //noinspection NullableProblems | |
117 | 22 | this(algorithmName, source, null, DEFAULT_ITERATIONS); |
118 | 22 | } |
119 | ||
120 | /** | |
121 | * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given {@code salt} | |
122 | * using a single hash iteration. | |
123 | * <p/> | |
124 | * It is a convenience constructor that merely executes <code>this( algorithmName, source, salt, 1);</code>. | |
125 | * <p/> | |
126 | * Please see the | |
127 | * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) SimpleHashHash(algorithmName, Object,Object,int)} | |
128 | * constructor for the types of Objects that may be passed into this constructor, as well as how to support further | |
129 | * types. | |
130 | * | |
131 | * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when | |
132 | * performing the hash. | |
133 | * @param source the source object to be hashed. | |
134 | * @param salt the salt to use for the hash | |
135 | * @throws CodecException if either constructor argument cannot be converted into a byte array. | |
136 | * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. | |
137 | */ | |
138 | public SimpleHash(String algorithmName, Object source, Object salt) throws CodecException, UnknownAlgorithmException { | |
139 | 6 | this(algorithmName, source, salt, DEFAULT_ITERATIONS); |
140 | 6 | } |
141 | ||
142 | /** | |
143 | * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given | |
144 | * {@code salt} a total of {@code hashIterations} times. | |
145 | * <p/> | |
146 | * By default, this class only supports Object method arguments of | |
147 | * type {@code byte[]}, {@code char[]}, {@link String}, {@link java.io.File File}, | |
148 | * {@link java.io.InputStream InputStream} or {@link org.apache.shiro.util.ByteSource ByteSource}. If either | |
149 | * argument is anything other than these types a {@link org.apache.shiro.codec.CodecException CodecException} | |
150 | * will be thrown. | |
151 | * <p/> | |
152 | * If you want to be able to hash other object types, or use other salt types, you need to override the | |
153 | * {@link #toBytes(Object) toBytes(Object)} method to support those specific types. Your other option is to | |
154 | * convert your arguments to one of the default supported types first before passing them in to this | |
155 | * constructor}. | |
156 | * | |
157 | * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when | |
158 | * performing the hash. | |
159 | * @param source the source object to be hashed. | |
160 | * @param salt the salt to use for the hash | |
161 | * @param hashIterations the number of times the {@code source} argument hashed for attack resiliency. | |
162 | * @throws CodecException if either Object constructor argument cannot be converted into a byte array. | |
163 | * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. | |
164 | */ | |
165 | public SimpleHash(String algorithmName, Object source, Object salt, int hashIterations) | |
166 | 81 | throws CodecException, UnknownAlgorithmException { |
167 | 81 | if (!StringUtils.hasText(algorithmName)) { |
168 | 0 | throw new NullPointerException("algorithmName argument cannot be null or empty."); |
169 | } | |
170 | 81 | this.algorithmName = algorithmName; |
171 | 81 | this.iterations = Math.max(DEFAULT_ITERATIONS, hashIterations); |
172 | 81 | ByteSource saltBytes = null; |
173 | 81 | if (salt != null) { |
174 | 37 | saltBytes = convertSaltToBytes(salt); |
175 | 37 | this.salt = saltBytes; |
176 | } | |
177 | 81 | ByteSource sourceBytes = convertSourceToBytes(source); |
178 | 81 | hash(sourceBytes, saltBytes, hashIterations); |
179 | 81 | } |
180 | ||
181 | /** | |
182 | * Acquires the specified {@code source} argument's bytes and returns them in the form of a {@code ByteSource} instance. | |
183 | * <p/> | |
184 | * This implementation merely delegates to the convenience {@link #toByteSource(Object)} method for generic | |
185 | * conversion. Can be overridden by subclasses for source-specific conversion. | |
186 | * | |
187 | * @param source the source object to be hashed. | |
188 | * @return the source's bytes in the form of a {@code ByteSource} instance. | |
189 | * @since 1.2 | |
190 | */ | |
191 | protected ByteSource convertSourceToBytes(Object source) { | |
192 | 81 | return toByteSource(source); |
193 | } | |
194 | ||
195 | /** | |
196 | * Acquires the specified {@code salt} argument's bytes and returns them in the form of a {@code ByteSource} instance. | |
197 | * <p/> | |
198 | * This implementation merely delegates to the convenience {@link #toByteSource(Object)} method for generic | |
199 | * conversion. Can be overridden by subclasses for salt-specific conversion. | |
200 | * | |
201 | * @param salt the salt to be use for the hash. | |
202 | * @return the salt's bytes in the form of a {@code ByteSource} instance. | |
203 | * @since 1.2 | |
204 | */ | |
205 | protected ByteSource convertSaltToBytes(Object salt) { | |
206 | 37 | return toByteSource(salt); |
207 | } | |
208 | ||
209 | /** | |
210 | * Converts a given object into a {@code ByteSource} instance. Assumes the object can be converted to bytes. | |
211 | * | |
212 | * @param o the Object to convert into a {@code ByteSource} instance. | |
213 | * @return the {@code ByteSource} representation of the specified object's bytes. | |
214 | * @since 1.2 | |
215 | */ | |
216 | protected ByteSource toByteSource(Object o) { | |
217 | 118 | if (o == null) { |
218 | 0 | return null; |
219 | } | |
220 | 118 | if (o instanceof ByteSource) { |
221 | 62 | return (ByteSource) o; |
222 | } | |
223 | 56 | byte[] bytes = toBytes(o); |
224 | 56 | return ByteSource.Util.bytes(bytes); |
225 | } | |
226 | ||
227 | private void hash(ByteSource source, ByteSource salt, int hashIterations) throws CodecException, UnknownAlgorithmException { | |
228 | 81 | byte[] saltBytes = salt != null ? salt.getBytes() : null; |
229 | 81 | byte[] hashedBytes = hash(source.getBytes(), saltBytes, hashIterations); |
230 | 81 | setBytes(hashedBytes); |
231 | 81 | } |
232 | ||
233 | /** | |
234 | * Returns the {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. | |
235 | * | |
236 | * @return the {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. | |
237 | */ | |
238 | public String getAlgorithmName() { | |
239 | 102 | return this.algorithmName; |
240 | } | |
241 | ||
242 | public ByteSource getSalt() { | |
243 | 22 | return this.salt; |
244 | } | |
245 | ||
246 | public int getIterations() { | |
247 | 21 | return this.iterations; |
248 | } | |
249 | ||
250 | public byte[] getBytes() { | |
251 | 137 | return this.bytes; |
252 | } | |
253 | ||
254 | /** | |
255 | * Sets the raw bytes stored by this hash instance. | |
256 | * <p/> | |
257 | * The bytes are kept in raw form - they will not be hashed/changed. This is primarily a utility method for | |
258 | * constructing a Hash instance when the hashed value is already known. | |
259 | * | |
260 | * @param alreadyHashedBytes the raw already-hashed bytes to store in this instance. | |
261 | */ | |
262 | public void setBytes(byte[] alreadyHashedBytes) { | |
263 | 135 | this.bytes = alreadyHashedBytes; |
264 | 135 | this.hexEncoded = null; |
265 | 135 | this.base64Encoded = null; |
266 | 135 | } |
267 | ||
268 | /** | |
269 | * Sets the iterations used to previously compute AN ALREADY GENERATED HASH. | |
270 | * <p/> | |
271 | * This is provided <em>ONLY</em> to reconstitute an already-created Hash instance. It should ONLY ever be | |
272 | * invoked when re-constructing a hash instance from an already-hashed value. | |
273 | * | |
274 | * @param iterations the number of hash iterations used to previously create the hash/digest. | |
275 | * @since 1.2 | |
276 | */ | |
277 | public void setIterations(int iterations) { | |
278 | 34 | this.iterations = Math.max(DEFAULT_ITERATIONS, iterations); |
279 | 34 | } |
280 | ||
281 | /** | |
282 | * Sets the salt used to previously compute AN ALREADY GENERATED HASH. | |
283 | * <p/> | |
284 | * This is provided <em>ONLY</em> to reconstitute a Hash instance that has already been computed. It should ONLY | |
285 | * ever be invoked when re-constructing a hash instance from an already-hashed value. | |
286 | * | |
287 | * @param salt the salt used to previously create the hash/digest. | |
288 | * @since 1.2 | |
289 | */ | |
290 | public void setSalt(ByteSource salt) { | |
291 | 33 | this.salt = salt; |
292 | 33 | } |
293 | ||
294 | /** | |
295 | * Returns the JDK MessageDigest instance to use for executing the hash. | |
296 | * | |
297 | * @param algorithmName the algorithm to use for the hash, provided by subclasses. | |
298 | * @return the MessageDigest object for the specified {@code algorithm}. | |
299 | * @throws UnknownAlgorithmException if the specified algorithm name is not available. | |
300 | */ | |
301 | protected MessageDigest getDigest(String algorithmName) throws UnknownAlgorithmException { | |
302 | try { | |
303 | 81 | return MessageDigest.getInstance(algorithmName); |
304 | 0 | } catch (NoSuchAlgorithmException e) { |
305 | 0 | String msg = "No native '" + algorithmName + "' MessageDigest instance available on the current JVM."; |
306 | 0 | throw new UnknownAlgorithmException(msg, e); |
307 | } | |
308 | } | |
309 | ||
310 | /** | |
311 | * Hashes the specified byte array without a salt for a single iteration. | |
312 | * | |
313 | * @param bytes the bytes to hash. | |
314 | * @return the hashed bytes. | |
315 | * @throws UnknownAlgorithmException if the configured {@link #getAlgorithmName() algorithmName} is not available. | |
316 | */ | |
317 | protected byte[] hash(byte[] bytes) throws UnknownAlgorithmException { | |
318 | 0 | return hash(bytes, null, DEFAULT_ITERATIONS); |
319 | } | |
320 | ||
321 | /** | |
322 | * Hashes the specified byte array using the given {@code salt} for a single iteration. | |
323 | * | |
324 | * @param bytes the bytes to hash | |
325 | * @param salt the salt to use for the initial hash | |
326 | * @return the hashed bytes | |
327 | * @throws UnknownAlgorithmException if the configured {@link #getAlgorithmName() algorithmName} is not available. | |
328 | */ | |
329 | protected byte[] hash(byte[] bytes, byte[] salt) throws UnknownAlgorithmException { | |
330 | 0 | return hash(bytes, salt, DEFAULT_ITERATIONS); |
331 | } | |
332 | ||
333 | /** | |
334 | * Hashes the specified byte array using the given {@code salt} for the specified number of iterations. | |
335 | * | |
336 | * @param bytes the bytes to hash | |
337 | * @param salt the salt to use for the initial hash | |
338 | * @param hashIterations the number of times the the {@code bytes} will be hashed (for attack resiliency). | |
339 | * @return the hashed bytes. | |
340 | * @throws UnknownAlgorithmException if the {@link #getAlgorithmName() algorithmName} is not available. | |
341 | */ | |
342 | protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException { | |
343 | 81 | MessageDigest digest = getDigest(getAlgorithmName()); |
344 | 81 | if (salt != null) { |
345 | 37 | digest.reset(); |
346 | 37 | digest.update(salt); |
347 | } | |
348 | 81 | byte[] hashed = digest.digest(bytes); |
349 | 81 | int iterations = hashIterations - DEFAULT_ITERATIONS; //already hashed once above |
350 | //iterate remaining number: | |
351 | 5000404 | for (int i = 0; i < iterations; i++) { |
352 | 5000323 | digest.reset(); |
353 | 5000323 | hashed = digest.digest(hashed); |
354 | } | |
355 | 81 | return hashed; |
356 | } | |
357 | ||
358 | public boolean isEmpty() { | |
359 | 6 | return this.bytes == null || this.bytes.length == 0; |
360 | } | |
361 | ||
362 | /** | |
363 | * Returns a hex-encoded string of the underlying {@link #getBytes byte array}. | |
364 | * <p/> | |
365 | * This implementation caches the resulting hex string so multiple calls to this method remain efficient. | |
366 | * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the | |
367 | * next time this method is called. | |
368 | * | |
369 | * @return a hex-encoded string of the underlying {@link #getBytes byte array}. | |
370 | */ | |
371 | public String toHex() { | |
372 | 17 | if (this.hexEncoded == null) { |
373 | 16 | this.hexEncoded = Hex.encodeToString(getBytes()); |
374 | } | |
375 | 17 | return this.hexEncoded; |
376 | } | |
377 | ||
378 | /** | |
379 | * Returns a Base64-encoded string of the underlying {@link #getBytes byte array}. | |
380 | * <p/> | |
381 | * This implementation caches the resulting Base64 string so multiple calls to this method remain efficient. | |
382 | * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the | |
383 | * next time this method is called. | |
384 | * | |
385 | * @return a Base64-encoded string of the underlying {@link #getBytes byte array}. | |
386 | */ | |
387 | public String toBase64() { | |
388 | 17 | if (this.base64Encoded == null) { |
389 | //cache result in case this method is called multiple times. | |
390 | 13 | this.base64Encoded = Base64.encodeToString(getBytes()); |
391 | } | |
392 | 17 | return this.base64Encoded; |
393 | } | |
394 | ||
395 | /** | |
396 | * Simple implementation that merely returns {@link #toHex() toHex()}. | |
397 | * | |
398 | * @return the {@link #toHex() toHex()} value. | |
399 | */ | |
400 | public String toString() { | |
401 | 1 | return toHex(); |
402 | } | |
403 | ||
404 | /** | |
405 | * Returns {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to | |
406 | * this Hash's byte array, {@code false} otherwise. | |
407 | * | |
408 | * @param o the object (Hash) to check for equality. | |
409 | * @return {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to | |
410 | * this Hash's byte array, {@code false} otherwise. | |
411 | */ | |
412 | public boolean equals(Object o) { | |
413 | 14 | if (o instanceof Hash) { |
414 | 14 | Hash other = (Hash) o; |
415 | 14 | return Arrays.equals(getBytes(), other.getBytes()); |
416 | } | |
417 | 0 | return false; |
418 | } | |
419 | ||
420 | /** | |
421 | * Simply returns toHex().hashCode(); | |
422 | * | |
423 | * @return toHex().hashCode() | |
424 | */ | |
425 | public int hashCode() { | |
426 | 0 | if (this.bytes == null || this.bytes.length == 0) { |
427 | 0 | return 0; |
428 | } | |
429 | 0 | return Arrays.hashCode(this.bytes); |
430 | } | |
431 | } |