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.stormpath.com/blog/strong-password-hashing-apache-shiro">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 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 public DefaultHashService() {
111 this.algorithmName = "SHA-512";
112 this.iterations = 1;
113 this.generatePublicSalt = false;
114 this.rng = new SecureRandomNumberGenerator();
115 }
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 if (request == null || request.getSource() == null || request.getSource().isEmpty()) {
152 return null;
153 }
154
155 String algorithmName = getAlgorithmName(request);
156 ByteSource source = request.getSource();
157 int iterations = getIterations(request);
158
159 ByteSource publicSalt = getPublicSalt(request);
160 ByteSource privateSalt = getPrivateSalt();
161 ByteSource salt = combine(privateSalt, publicSalt);
162
163 Hash computed = new SimpleHash(algorithmName, source, salt, iterations);
164
165 SimpleHashSimpleHash.html#SimpleHash">SimpleHash result = new SimpleHash(algorithmName);
166 result.setBytes(computed.getBytes());
167 result.setIterations(iterations);
168 //Only expose the public salt - not the real/combined salt that might have been used:
169 result.setSalt(publicSalt);
170
171 return result;
172 }
173
174 protected String getAlgorithmName(HashRequest request) {
175 String name = request.getAlgorithmName();
176 if (name == null) {
177 name = getHashAlgorithmName();
178 }
179 return name;
180 }
181
182 protected int getIterations(HashRequest request) {
183 int iterations = Math.max(0, request.getIterations());
184 if (iterations < 1) {
185 iterations = Math.max(1, getHashIterations());
186 }
187 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 ByteSource publicSalt = request.getSalt();
215
216 if (publicSalt != null && !publicSalt.isEmpty()) {
217 //a public salt was explicitly requested to be used - go ahead and use it:
218 return publicSalt;
219 }
220
221 publicSalt = null;
222
223 //check to see if we need to generate one:
224 ByteSource privateSalt = getPrivateSalt();
225 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 if (privateSaltExists || isGeneratePublicSalt()) {
230 publicSalt = getRandomNumberGenerator().nextBytes();
231 }
232
233 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./../../org/apache/shiro/util/ByteSource.html#ByteSource">ByteSource/../../../../org/apache/shiro/util/ByteSource.html#ByteSource">ByteSource combine(ByteSource./../../org/apache/shiro/util/ByteSource.html#ByteSource">ByteSource privateSalt, ByteSource publicSalt) {
247
248 byte[] privateSaltBytes = privateSalt != null ? privateSalt.getBytes() : null;
249 int privateSaltLength = privateSaltBytes != null ? privateSaltBytes.length : 0;
250
251 byte[] publicSaltBytes = publicSalt != null ? publicSalt.getBytes() : null;
252 int extraBytesLength = publicSaltBytes != null ? publicSaltBytes.length : 0;
253
254 int length = privateSaltLength + extraBytesLength;
255
256 if (length <= 0) {
257 return null;
258 }
259
260 byte[] combined = new byte[length];
261
262 int i = 0;
263 for (int j = 0; j < privateSaltLength; j++) {
264 assert privateSaltBytes != null;
265 combined[i++] = privateSaltBytes[j];
266 }
267 for (int j = 0; j < extraBytesLength; j++) {
268 assert publicSaltBytes != null;
269 combined[i++] = publicSaltBytes[j];
270 }
271
272 return ByteSource.Util.bytes(combined);
273 }
274
275 public void setHashAlgorithmName(String name) {
276 this.algorithmName = name;
277 }
278
279 public String getHashAlgorithmName() {
280 return this.algorithmName;
281 }
282
283 public void setPrivateSalt(ByteSource privateSalt) {
284 this.privateSalt = privateSalt;
285 }
286
287 public ByteSource getPrivateSalt() {
288 return this.privateSalt;
289 }
290
291 public void setHashIterations(int count) {
292 this.iterations = count;
293 }
294
295 public int getHashIterations() {
296 return this.iterations;
297 }
298
299 public void setRandomNumberGenerator(RandomNumberGenerator rng) {
300 this.rng = rng;
301 }
302
303 public RandomNumberGenerator getRandomNumberGenerator() {
304 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 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 this.generatePublicSalt = generatePublicSalt;
343 }
344 }