Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DefaultHashService |
|
| 2.25;2.25 |
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.RandomNumberGenerator; | |
22 | import org.apache.shiro.crypto.SecureRandomNumberGenerator; | |
23 | import org.apache.shiro.util.ByteSource; | |
24 | ||
25 | /** | |
26 | * Default implementation of the {@link HashService} interface, supporting a customizable hash algorithm name, | |
27 | * secure-random salt generation, multiple hash iterations and an optional internal | |
28 | * {@link #setPrivateSalt(ByteSource) privateSalt}. | |
29 | * <h2>Hash Algorithm</h2> | |
30 | * You may specify a hash algorithm via the {@link #setHashAlgorithmName(String)} property. Any algorithm name | |
31 | * understood by the JDK | |
32 | * {@link java.security.MessageDigest#getInstance(String) MessageDigest.getInstance(String algorithmName)} method | |
33 | * will work. The default is {@code SHA-512}. | |
34 | * <h2>Random Salts</h2> | |
35 | * When a salt is not specified in a request, this implementation generates secure random salts via its | |
36 | * {@link #setRandomNumberGenerator(org.apache.shiro.crypto.RandomNumberGenerator) randomNumberGenerator} property. | |
37 | * Random salts (and potentially combined with the internal {@link #getPrivateSalt() privateSalt}) is a very strong | |
38 | * salting strategy, as salts should ideally never be based on known/guessable data. The default instance is a | |
39 | * {@link SecureRandomNumberGenerator}. | |
40 | * <h2>Hash Iterations</h2> | |
41 | * Secure hashing strategies often employ multiple hash iterations to slow down the hashing process. This technique | |
42 | * is usually used for password hashing, since the longer it takes to compute a password hash, the longer it would | |
43 | * take for an attacker to compromise a password. This | |
44 | * <a href="http://www.katasoft.com/blog/2011/04/04/strong-password-hashing-apache-shiro">Katasoft blog article</a> | |
45 | * explains in greater detail why this is useful, as well as information on how many iterations is 'enough'. | |
46 | * <p/> | |
47 | * You may set the number of hash iterations via the {@link #setHashIterations(int)} property. The default is | |
48 | * {@code 1}, but should be increased significantly if the {@code HashService} is intended to be used for password | |
49 | * hashing. See the linked blog article for more info. | |
50 | * <h2>Private Salt</h2> | |
51 | * If using this implementation as part of a password hashing strategy, it might be desirable to configure a | |
52 | * {@link #setPrivateSalt(ByteSource) private salt}: | |
53 | * <p/> | |
54 | * A hash and the salt used to compute it are often stored together. If an attacker is ever able to access | |
55 | * the hash (e.g. during password cracking) and it has the full salt value, the attacker has all of the input necessary | |
56 | * to try to brute-force crack the hash (source + complete salt). | |
57 | * <p/> | |
58 | * However, if part of the salt is not available to the attacker (because it is not stored with the hash), it is | |
59 | * <em>much</em> harder to crack the hash value since the attacker does not have the complete inputs necessary. | |
60 | * <p/> | |
61 | * The {@link #getPrivateSalt() privateSalt} property exists to satisfy this private-and-not-shared part of the salt. | |
62 | * If you configure this attribute, you can obtain this additional very important safety feature. | |
63 | * <p/> | |
64 | * <b>*</b>By default, the {@link #getPrivateSalt() privateSalt} is null, since a sensible default cannot be used that | |
65 | * isn't easily compromised (because Shiro is an open-source project and any default could be easily seen and used). | |
66 | * | |
67 | * @since 1.2 | |
68 | */ | |
69 | 1 | public class DefaultHashService implements ConfigurableHashService { |
70 | ||
71 | /** | |
72 | * The RandomNumberGenerator to use to randomly generate the public part of the hash salt. | |
73 | */ | |
74 | private RandomNumberGenerator rng; | |
75 | ||
76 | /** | |
77 | * The MessageDigest name of the hash algorithm to use for computing hashes. | |
78 | */ | |
79 | private String algorithmName; | |
80 | ||
81 | /** | |
82 | * The 'private' part of the hash salt. | |
83 | */ | |
84 | private ByteSource privateSalt; | |
85 | ||
86 | /** | |
87 | * The number of hash iterations to perform when computing hashes. | |
88 | */ | |
89 | private int iterations; | |
90 | ||
91 | /** | |
92 | * Whether or not to generate public salts if a request does not provide one. | |
93 | */ | |
94 | private boolean generatePublicSalt; | |
95 | ||
96 | /** | |
97 | * Constructs a new {@code DefaultHashService} instance with the following defaults: | |
98 | * <ul> | |
99 | * <li>{@link #setHashAlgorithmName(String) hashAlgorithmName} = {@code SHA-512}</li> | |
100 | * <li>{@link #setHashIterations(int) hashIterations} = {@code 1}</li> | |
101 | * <li>{@link #setRandomNumberGenerator(org.apache.shiro.crypto.RandomNumberGenerator) randomNumberGenerator} = | |
102 | * new {@link SecureRandomNumberGenerator}()</li> | |
103 | * <li>{@link #setGeneratePublicSalt(boolean) generatePublicSalt} = {@code false}</li> | |
104 | * </ul> | |
105 | * <p/> | |
106 | * If this hashService will be used for password hashing it is recommended to set the | |
107 | * {@link #setPrivateSalt(ByteSource) privateSalt} and significantly increase the number of | |
108 | * {@link #setHashIterations(int) hashIterations}. See the class-level JavaDoc for more information. | |
109 | */ | |
110 | 28 | public DefaultHashService() { |
111 | 28 | this.algorithmName = "SHA-512"; |
112 | 28 | this.iterations = 1; |
113 | 28 | this.generatePublicSalt = false; |
114 | 28 | this.rng = new SecureRandomNumberGenerator(); |
115 | 28 | } |
116 | ||
117 | /** | |
118 | * Computes and responds with a hash based on the specified request. | |
119 | * <p/> | |
120 | * This implementation functions as follows: | |
121 | * <ul> | |
122 | * <li>If the request's {@link org.apache.shiro.crypto.hash.HashRequest#getSalt() salt} is null: | |
123 | * <p/> | |
124 | * A salt will be generated and used to compute the hash. The salt is generated as follows: | |
125 | * <ol> | |
126 | * <li>Use the {@link #getRandomNumberGenerator() randomNumberGenerator} to generate a new random number.</li> | |
127 | * <li>{@link #combine(ByteSource, ByteSource) combine} this random salt with any configured | |
128 | * {@link #getPrivateSalt() privateSalt} | |
129 | * </li> | |
130 | * <li>Use the combined value as the salt used during hash computation</li> | |
131 | * </ol> | |
132 | * </li> | |
133 | * <li> | |
134 | * If the request salt is not null: | |
135 | * <p/> | |
136 | * This indicates that the hash computation is for comparison purposes (of a | |
137 | * previously computed hash). The request salt will be {@link #combine(ByteSource, ByteSource) combined} with any | |
138 | * configured {@link #getPrivateSalt() privateSalt} and used as the complete salt during hash computation. | |
139 | * </li> | |
140 | * </ul> | |
141 | * <p/> | |
142 | * The returned {@code Hash}'s {@link Hash#getSalt() salt} property | |
143 | * will contain <em>only</em> the 'public' part of the salt and <em>NOT</em> the privateSalt. See the class-level | |
144 | * JavaDoc explanation for more info. | |
145 | * | |
146 | * @param request the request to process | |
147 | * @return the response containing the result of the hash computation, as well as any hash salt used that should be | |
148 | * exposed to the caller. | |
149 | */ | |
150 | public Hash computeHash(HashRequest request) { | |
151 | 29 | if (request == null || request.getSource() == null || request.getSource().isEmpty()) { |
152 | 2 | return null; |
153 | } | |
154 | ||
155 | 27 | String algorithmName = getAlgorithmName(request); |
156 | 27 | ByteSource source = request.getSource(); |
157 | 27 | int iterations = getIterations(request); |
158 | ||
159 | 27 | ByteSource publicSalt = getPublicSalt(request); |
160 | 27 | ByteSource privateSalt = getPrivateSalt(); |
161 | 27 | ByteSource salt = combine(privateSalt, publicSalt); |
162 | ||
163 | 27 | Hash computed = new SimpleHash(algorithmName, source, salt, iterations); |
164 | ||
165 | 27 | SimpleHash result = new SimpleHash(algorithmName); |
166 | 27 | result.setBytes(computed.getBytes()); |
167 | 27 | result.setIterations(iterations); |
168 | //Only expose the public salt - not the real/combined salt that might have been used: | |
169 | 27 | result.setSalt(publicSalt); |
170 | ||
171 | 27 | return result; |
172 | } | |
173 | ||
174 | protected String getAlgorithmName(HashRequest request) { | |
175 | 27 | String name = request.getAlgorithmName(); |
176 | 27 | if (name == null) { |
177 | 22 | name = getHashAlgorithmName(); |
178 | } | |
179 | 27 | return name; |
180 | } | |
181 | ||
182 | protected int getIterations(HashRequest request) { | |
183 | 27 | int iterations = Math.max(0, request.getIterations()); |
184 | 27 | if (iterations < 1) { |
185 | 22 | iterations = Math.max(1, getHashIterations()); |
186 | } | |
187 | 27 | return iterations; |
188 | } | |
189 | ||
190 | /** | |
191 | * Returns the public salt that should be used to compute a hash based on the specified request or | |
192 | * {@code null} if no public salt should be used. | |
193 | * <p/> | |
194 | * This implementation functions as follows: | |
195 | * <ol> | |
196 | * <li>If the request salt is not null and non-empty, this will be used, return it.</li> | |
197 | * <li>If the request salt is null or empty: | |
198 | * <ol> | |
199 | * <li>If a private salt has been set <em>OR</em> {@link #isGeneratePublicSalt()} is {@code true}, | |
200 | * auto generate a random public salt via the configured | |
201 | * {@link #getRandomNumberGenerator() randomNumberGenerator}.</li> | |
202 | * <li>If a private salt has not been configured and {@link #isGeneratePublicSalt()} is {@code false}, | |
203 | * do nothing - return {@code null} to indicate a salt should not be used during hash computation.</li> | |
204 | * </ol> | |
205 | * </li> | |
206 | * </ol> | |
207 | * | |
208 | * @param request request the request to process | |
209 | * @return the public salt that should be used to compute a hash based on the specified request or | |
210 | * {@code null} if no public salt should be used. | |
211 | */ | |
212 | protected ByteSource getPublicSalt(HashRequest request) { | |
213 | ||
214 | 27 | ByteSource publicSalt = request.getSalt(); |
215 | ||
216 | 27 | if (publicSalt != null && !publicSalt.isEmpty()) { |
217 | //a public salt was explicitly requested to be used - go ahead and use it: | |
218 | 9 | return publicSalt; |
219 | } | |
220 | ||
221 | 18 | publicSalt = null; |
222 | ||
223 | //check to see if we need to generate one: | |
224 | 18 | ByteSource privateSalt = getPrivateSalt(); |
225 | 18 | boolean privateSaltExists = privateSalt != null && !privateSalt.isEmpty(); |
226 | ||
227 | //If a private salt exists, we must generate a public salt to protect the integrity of the private salt. | |
228 | //Or generate it if the instance is explicitly configured to do so: | |
229 | 18 | if (privateSaltExists || isGeneratePublicSalt()) { |
230 | 12 | publicSalt = getRandomNumberGenerator().nextBytes(); |
231 | } | |
232 | ||
233 | 18 | return publicSalt; |
234 | } | |
235 | ||
236 | /** | |
237 | * Combines the specified 'private' salt bytes with the specified additional extra bytes to use as the | |
238 | * total salt during hash computation. {@code privateSaltBytes} will be {@code null} }if no private salt has been | |
239 | * configured. | |
240 | * | |
241 | * @param privateSalt the (possibly {@code null}) 'private' salt to combine with the specified extra bytes | |
242 | * @param publicSalt the extra bytes to use in addition to the given private salt. | |
243 | * @return a combination of the specified private salt bytes and extra bytes that will be used as the total | |
244 | * salt during hash computation. | |
245 | */ | |
246 | protected ByteSource combine(ByteSource privateSalt, ByteSource publicSalt) { | |
247 | ||
248 | 27 | byte[] privateSaltBytes = privateSalt != null ? privateSalt.getBytes() : null; |
249 | 27 | int privateSaltLength = privateSaltBytes != null ? privateSaltBytes.length : 0; |
250 | ||
251 | 27 | byte[] publicSaltBytes = publicSalt != null ? publicSalt.getBytes() : null; |
252 | 27 | int extraBytesLength = publicSaltBytes != null ? publicSaltBytes.length : 0; |
253 | ||
254 | 27 | int length = privateSaltLength + extraBytesLength; |
255 | ||
256 | 27 | if (length <= 0) { |
257 | 6 | return null; |
258 | } | |
259 | ||
260 | 21 | byte[] combined = new byte[length]; |
261 | ||
262 | 21 | int i = 0; |
263 | 133 | for (int j = 0; j < privateSaltLength; j++) { |
264 | 112 | assert privateSaltBytes != null; |
265 | 112 | combined[i++] = privateSaltBytes[j]; |
266 | } | |
267 | 365 | for (int j = 0; j < extraBytesLength; j++) { |
268 | 344 | assert publicSaltBytes != null; |
269 | 344 | combined[i++] = publicSaltBytes[j]; |
270 | } | |
271 | ||
272 | 21 | return ByteSource.Util.bytes(combined); |
273 | } | |
274 | ||
275 | public void setHashAlgorithmName(String name) { | |
276 | 18 | this.algorithmName = name; |
277 | 18 | } |
278 | ||
279 | public String getHashAlgorithmName() { | |
280 | 22 | return this.algorithmName; |
281 | } | |
282 | ||
283 | public void setPrivateSalt(ByteSource privateSalt) { | |
284 | 4 | this.privateSalt = privateSalt; |
285 | 4 | } |
286 | ||
287 | public ByteSource getPrivateSalt() { | |
288 | 45 | return this.privateSalt; |
289 | } | |
290 | ||
291 | public void setHashIterations(int count) { | |
292 | 18 | this.iterations = count; |
293 | 18 | } |
294 | ||
295 | public int getHashIterations() { | |
296 | 22 | return this.iterations; |
297 | } | |
298 | ||
299 | public void setRandomNumberGenerator(RandomNumberGenerator rng) { | |
300 | 1 | this.rng = rng; |
301 | 1 | } |
302 | ||
303 | public RandomNumberGenerator getRandomNumberGenerator() { | |
304 | 12 | return this.rng; |
305 | } | |
306 | ||
307 | /** | |
308 | * Returns {@code true} if a public salt should be randomly generated and used to compute a hash if a | |
309 | * {@link HashRequest} does not specify a salt, {@code false} otherwise. | |
310 | * <p/> | |
311 | * The default value is {@code false} but should definitely be set to {@code true} if the | |
312 | * {@code HashService} instance is being used for password hashing. | |
313 | * <p/> | |
314 | * <b>NOTE:</b> this property only has an effect if a {@link #getPrivateSalt() privateSalt} is NOT configured. If a | |
315 | * private salt has been configured and a request does not provide a salt, a random salt will always be generated | |
316 | * to protect the integrity of the private salt (without a public salt, the private salt would be exposed as-is, | |
317 | * which is undesirable). | |
318 | * | |
319 | * @return {@code true} if a public salt should be randomly generated and used to compute a hash if a | |
320 | * {@link HashRequest} does not specify a salt, {@code false} otherwise. | |
321 | */ | |
322 | public boolean isGeneratePublicSalt() { | |
323 | 14 | return generatePublicSalt; |
324 | } | |
325 | ||
326 | /** | |
327 | * Sets whether or not a public salt should be randomly generated and used to compute a hash if a | |
328 | * {@link HashRequest} does not specify a salt. | |
329 | * <p/> | |
330 | * The default value is {@code false} but should definitely be set to {@code true} if the | |
331 | * {@code HashService} instance is being used for password hashing. | |
332 | * <p/> | |
333 | * <b>NOTE:</b> this property only has an effect if a {@link #getPrivateSalt() privateSalt} is NOT configured. If a | |
334 | * private salt has been configured and a request does not provide a salt, a random salt will always be generated | |
335 | * to protect the integrity of the private salt (without a public salt, the private salt would be exposed as-is, | |
336 | * which is undesirable). | |
337 | * | |
338 | * @param generatePublicSalt whether or not a public salt should be randomly generated and used to compute a hash | |
339 | * if a {@link HashRequest} does not specify a salt. | |
340 | */ | |
341 | public void setGeneratePublicSalt(boolean generatePublicSalt) { | |
342 | 19 | this.generatePublicSalt = generatePublicSalt; |
343 | 19 | } |
344 | } |