Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
HashedCredentialsMatcher |
|
| 1.588235294117647;1.588 |
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.authc.credential; | |
20 | ||
21 | import org.apache.shiro.authc.AuthenticationInfo; | |
22 | import org.apache.shiro.authc.AuthenticationToken; | |
23 | import org.apache.shiro.authc.SaltedAuthenticationInfo; | |
24 | import org.apache.shiro.codec.Base64; | |
25 | import org.apache.shiro.codec.Hex; | |
26 | import org.apache.shiro.crypto.hash.AbstractHash; | |
27 | import org.apache.shiro.crypto.hash.Hash; | |
28 | import org.apache.shiro.crypto.hash.SimpleHash; | |
29 | import org.apache.shiro.util.StringUtils; | |
30 | ||
31 | /** | |
32 | * A {@code HashedCredentialMatcher} provides support for hashing of supplied {@code AuthenticationToken} credentials | |
33 | * before being compared to those in the {@code AuthenticationInfo} from the data store. | |
34 | * <p/> | |
35 | * Credential hashing is one of the most common security techniques when safeguarding a user's private credentials | |
36 | * (passwords, keys, etc). Most developers never want to store their users' credentials in plain form, viewable by | |
37 | * anyone, so they often hash the users' credentials before they are saved in the data store. | |
38 | * <p/> | |
39 | * This class (and its subclasses) function as follows: | |
40 | * <ol> | |
41 | * <li>Hash the {@code AuthenticationToken} credentials supplied by the user during their login.</li> | |
42 | * <li>Compare this hashed value directly with the {@code AuthenticationInfo} credentials stored in the system | |
43 | * (the stored account credentials are expected to already be in hashed form).</li> | |
44 | * <li>If these two values are {@link #equals(Object, Object) equal}, the submitted credentials match, otherwise | |
45 | * they do not.</li> | |
46 | * </ol> | |
47 | * <h2>Salting and Multiple Hash Iterations</h2> | |
48 | * Because simple hashing is usually not good enough for secure applications, this class also supports 'salting' | |
49 | * and multiple hash iterations. Please read this excellent | |
50 | * <a href="http://www.owasp.org/index.php/Hashing_Java" _target="blank">Hashing Java article</a> to learn about | |
51 | * salting and multiple iterations and why you might want to use them. (Note of sections 5 | |
52 | * "Why add salt?" and 6 "Hardening against the attacker's attack"). We should also note here that all of | |
53 | * Shiro's Hash implementations (for example, {@link org.apache.shiro.crypto.hash.Md5Hash Md5Hash}, | |
54 | * {@link org.apache.shiro.crypto.hash.Sha1Hash Sha1Hash}, etc) support salting and multiple hash iterations via | |
55 | * overloaded constructors. | |
56 | * <h4>Real World Case Study</h4> | |
57 | * In April 2010, some public Atlassian Jira and Confluence | |
58 | * installations (Apache Software Foundation, Codehaus, etc) were the target of account attacks and user accounts | |
59 | * were compromised. The reason? Jira and Confluence at the time did not salt user passwords and attackers were | |
60 | * able to use dictionary attacks to compromise user accounts (Atlassian has since | |
61 | * <a href="http://blogs.atlassian.com/news/2010/04/oh_man_what_a_day_an_update_on_our_security_breach.html"> | |
62 | * fixed the problem</a> of course). | |
63 | * <p/> | |
64 | * The lesson? | |
65 | * <p/> | |
66 | * <b>ALWAYS, ALWAYS, ALWAYS SALT USER PASSWORDS!</b> | |
67 | * <p/> | |
68 | * <h3>Salting</h3> | |
69 | * Prior to Shiro 1.1, salts could be obtained based on the end-user submitted | |
70 | * {@link AuthenticationToken AuthenticationToken} via the now-deprecated | |
71 | * {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getSalt(AuthenticationToken)} method. This however | |
72 | * could constitute a security hole since ideally salts should never be obtained based on what a user can submit. | |
73 | * User-submitted salt mechanisms are <em>much</em> more susceptible to dictionary attacks and <b>SHOULD NOT</b> be | |
74 | * used in secure systems. Instead salts should ideally be a secure randomly-generated number that is generated when | |
75 | * the user account is created. The secure number should never be disseminated to the user and always kept private | |
76 | * by the application. | |
77 | * <h4>Shiro 1.1</h4> | |
78 | * As of Shiro 1.1, it is expected that any salt used to hash the submitted credentials will be obtained from the | |
79 | * stored account information (represented as an {@link AuthenticationInfo AuthenticationInfo} instance). This is much | |
80 | * more secure because the salt value remains private to the application (Shiro will never store this value). | |
81 | * <p/> | |
82 | * To enable this, {@code Realm}s should return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances | |
83 | * during authentication. {@code HashedCredentialsMatcher} implementations will then use the provided | |
84 | * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt credentialsSalt} for hashing. To avoid | |
85 | * security risks, | |
86 | * <b>it is highly recommended that any existing {@code Realm} implementations that support hashed credentials are | |
87 | * updated to return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances as soon as possible</b>. | |
88 | * <h4>Shiro 1.0 Backwards Compatibility</h4> | |
89 | * Because of the identified security risk, {@code Realm} implementations that support credentials hashing should | |
90 | * be updated to return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances as | |
91 | * soon as possible. | |
92 | * <p/> | |
93 | * If this is not possible for some reason, this class will retain 1.0 backwards-compatible behavior of obtaining | |
94 | * the salt via the now-deprecated {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)} method. This | |
95 | * method will only be invoked if a {@code Realm} <em>does not</em> return | |
96 | * {@link SaltedAuthenticationInfo SaltedAutenticationInfo} instances and {@link #isHashSalted() hashSalted} is | |
97 | * {@code true}. | |
98 | * But please note that the {@link #isHashSalted() hashSalted} property and the | |
99 | * {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)} methods will be removed before the Shiro 2.0 | |
100 | * release. | |
101 | * <h3>Multiple Hash Iterations</h3> | |
102 | * If you hash your users' credentials multiple times before persisting to the data store, you will also need to | |
103 | * set this class's {@link #setHashIterations(int) hashIterations} property. See the | |
104 | * <a href="http://www.owasp.org/index.php/Hashing_Java" _target="blank">Hashing Java article</a>'s | |
105 | * <a href="http://www.owasp.org/index.php/Hashing_Java#Hardening_against_the_attacker.27s_attack"> | |
106 | * "Hardening against the attacker's attack"</a> section to learn more about why you might want to use | |
107 | * multiple hash iterations. | |
108 | * <h2>MD5 & SHA-1 Notice</h2> | |
109 | * <a href="http://en.wikipedia.org/wiki/MD5">MD5</a> and | |
110 | * <a href="http://en.wikipedia.org/wiki/SHA_hash_functions">SHA-1</a> algorithms are now known to be vulnerable to | |
111 | * compromise and/or collisions (read the linked pages for more). While most applications are ok with either of these | |
112 | * two, if your application mandates high security, use the SHA-256 (or higher) hashing algorithms and their | |
113 | * supporting {@code CredentialsMatcher} implementations. | |
114 | * | |
115 | * @see org.apache.shiro.crypto.hash.Md5Hash | |
116 | * @see org.apache.shiro.crypto.hash.Sha1Hash | |
117 | * @see org.apache.shiro.crypto.hash.Sha256Hash | |
118 | * @since 0.9 | |
119 | */ | |
120 | public class HashedCredentialsMatcher extends SimpleCredentialsMatcher { | |
121 | ||
122 | /** | |
123 | * @since 1.1 | |
124 | */ | |
125 | private String hashAlgorithm; | |
126 | private int hashIterations; | |
127 | private boolean hashSalted; | |
128 | private boolean storedCredentialsHexEncoded; | |
129 | ||
130 | /** | |
131 | * JavaBeans-compatibile no-arg constructor intended for use in IoC/Dependency Injection environments. If you | |
132 | * use this constructor, you <em>MUST</em> also additionally set the | |
133 | * {@link #setHashAlgorithmName(String) hashAlgorithmName} property. | |
134 | */ | |
135 | 21 | public HashedCredentialsMatcher() { |
136 | 21 | this.hashAlgorithm = null; |
137 | 21 | this.hashSalted = false; |
138 | 21 | this.hashIterations = 1; |
139 | 21 | this.storedCredentialsHexEncoded = true; //false means Base64-encoded |
140 | 21 | } |
141 | ||
142 | /** | |
143 | * Creates an instance using the specified {@link #getHashAlgorithmName() hashAlgorithmName} to hash submitted | |
144 | * credentials. | |
145 | * @param hashAlgorithmName the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} | |
146 | * to use when performing hashes for credentials matching. | |
147 | * @since 1.1 | |
148 | */ | |
149 | public HashedCredentialsMatcher(String hashAlgorithmName) { | |
150 | 3 | this(); |
151 | 3 | if (!StringUtils.hasText(hashAlgorithmName) ) { |
152 | 0 | throw new IllegalArgumentException("hashAlgorithmName cannot be null or empty."); |
153 | } | |
154 | 3 | this.hashAlgorithm = hashAlgorithmName; |
155 | 3 | } |
156 | ||
157 | /** | |
158 | * Returns the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use | |
159 | * when performing hashes for credentials matching. | |
160 | * | |
161 | * @return the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use | |
162 | * when performing hashes for credentials matching. | |
163 | * @since 1.1 | |
164 | */ | |
165 | public String getHashAlgorithmName() { | |
166 | 40 | return hashAlgorithm; |
167 | } | |
168 | ||
169 | /** | |
170 | * Sets the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use | |
171 | * when performing hashes for credentials matching. | |
172 | * | |
173 | * @param hashAlgorithmName the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} | |
174 | * to use when performing hashes for credentials matching. | |
175 | * @since 1.1 | |
176 | */ | |
177 | public void setHashAlgorithmName(String hashAlgorithmName) { | |
178 | 18 | this.hashAlgorithm = hashAlgorithmName; |
179 | 18 | } |
180 | ||
181 | /** | |
182 | * Returns {@code true} if the system's stored credential hash is Hex encoded, {@code false} if it | |
183 | * is Base64 encoded. | |
184 | * <p/> | |
185 | * Default value is {@code true} for convenience - all of Shiro's {@link Hash Hash#toString()} | |
186 | * implementations return Hex encoded values by default, making this class's use with those implementations | |
187 | * easier. | |
188 | * | |
189 | * @return {@code true} if the system's stored credential hash is Hex encoded, {@code false} if it | |
190 | * is Base64 encoded. Default is {@code true} | |
191 | */ | |
192 | public boolean isStoredCredentialsHexEncoded() { | |
193 | 11 | return storedCredentialsHexEncoded; |
194 | } | |
195 | ||
196 | /** | |
197 | * Sets the indicator if this system's stored credential hash is Hex encoded or not. | |
198 | * <p/> | |
199 | * A value of {@code true} will cause this class to decode the system credential from Hex, a | |
200 | * value of {@code false} will cause this class to decode the system credential from Base64. | |
201 | * <p/> | |
202 | * Unless overridden via this method, the default value is {@code true} for convenience - all of Shiro's | |
203 | * {@link Hash Hash#toString()} implementations return Hex encoded values by default, making this class's use with | |
204 | * those implementations easier. | |
205 | * | |
206 | * @param storedCredentialsHexEncoded the indicator if this system's stored credential hash is Hex | |
207 | * encoded or not ('not' automatically implying it is Base64 encoded). | |
208 | */ | |
209 | public void setStoredCredentialsHexEncoded(boolean storedCredentialsHexEncoded) { | |
210 | 0 | this.storedCredentialsHexEncoded = storedCredentialsHexEncoded; |
211 | 0 | } |
212 | ||
213 | /** | |
214 | * Returns {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing, | |
215 | * {@code false} if it should not be salted. | |
216 | * <p/> | |
217 | * If enabled, the salt used will be obtained via the {@link #getSalt(AuthenticationToken) getSalt} method. | |
218 | * <p/> | |
219 | * The default value is {@code false}. | |
220 | * | |
221 | * @return {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing, | |
222 | * {@code false} if it should not be salted. | |
223 | * @deprecated since Shiro 1.1. Hash salting is now expected to be based on if the {@link AuthenticationInfo} | |
224 | * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its | |
225 | * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value. | |
226 | * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return | |
227 | * {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations | |
228 | * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} | |
229 | * instances as soon as possible</b>. | |
230 | * <p/> | |
231 | * This is because salts should always be obtained from the stored account information and | |
232 | * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for | |
233 | * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user | |
234 | * are almost impossible to break. This method will be removed in Shiro 2.0. | |
235 | */ | |
236 | @Deprecated | |
237 | public boolean isHashSalted() { | |
238 | 2 | return hashSalted; |
239 | } | |
240 | ||
241 | /** | |
242 | * Sets whether or not to salt a submitted {@code AuthenticationToken}'s credentials when hashing. | |
243 | * <p/> | |
244 | * If enabled, the salt used will be obtained via the {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getCredentialsSalt} method. | |
245 | * </p> | |
246 | * The default value is {@code false}. | |
247 | * | |
248 | * @param hashSalted whether or not to salt a submitted {@code AuthenticationToken}'s credentials when hashing. | |
249 | * @deprecated since Shiro 1.1. Hash salting is now expected to be based on if the {@link AuthenticationInfo} | |
250 | * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its | |
251 | * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value. | |
252 | * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return | |
253 | * {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations | |
254 | * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} | |
255 | * instances as soon as possible</b>. | |
256 | * <p/> | |
257 | * This is because salts should always be obtained from the stored account information and | |
258 | * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for | |
259 | * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user | |
260 | * are almost impossible to break. This method will be removed in Shiro 2.0. | |
261 | */ | |
262 | @Deprecated | |
263 | public void setHashSalted(boolean hashSalted) { | |
264 | 1 | this.hashSalted = hashSalted; |
265 | 1 | } |
266 | ||
267 | /** | |
268 | * Returns the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before | |
269 | * comparing to the credentials stored in the system. | |
270 | * <p/> | |
271 | * Unless overridden, the default value is {@code 1}, meaning a normal hash execution will occur. | |
272 | * | |
273 | * @return the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before | |
274 | * comparing to the credentials stored in the system. | |
275 | */ | |
276 | public int getHashIterations() { | |
277 | 20 | return hashIterations; |
278 | } | |
279 | ||
280 | /** | |
281 | * Sets the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before comparing | |
282 | * to the credentials stored in the system. | |
283 | * <p/> | |
284 | * Unless overridden, the default value is {@code 1}, meaning a normal single hash execution will occur. | |
285 | * <p/> | |
286 | * If this argument is less than 1 (i.e. 0 or negative), the default value of 1 is applied. There must always be | |
287 | * at least 1 hash iteration (otherwise there would be no hash). | |
288 | * | |
289 | * @param hashIterations the number of times to hash a submitted {@code AuthenticationToken}'s credentials. | |
290 | */ | |
291 | public void setHashIterations(int hashIterations) { | |
292 | 0 | if (hashIterations < 1) { |
293 | 0 | this.hashIterations = 1; |
294 | } else { | |
295 | 0 | this.hashIterations = hashIterations; |
296 | } | |
297 | 0 | } |
298 | ||
299 | /** | |
300 | * Returns a salt value used to hash the token's credentials. | |
301 | * <p/> | |
302 | * This default implementation merely returns {@code token.getPrincipal()}, effectively using the user's | |
303 | * identity (username, user id, etc) as the salt, a most common technique. If you wish to provide the | |
304 | * authentication token's salt another way, you may override this method. | |
305 | * | |
306 | * @param token the AuthenticationToken submitted during the authentication attempt. | |
307 | * @return a salt value to use to hash the authentication token's credentials. | |
308 | * @deprecated since Shiro 1.1. Hash salting is now expected to be based on if the {@link AuthenticationInfo} | |
309 | * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its | |
310 | * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value. | |
311 | * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return | |
312 | * {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations | |
313 | * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} | |
314 | * instances as soon as possible</b>.<p/> | |
315 | * This is because salts should always be obtained from the stored account information and | |
316 | * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for | |
317 | * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user | |
318 | * are almost impossible to break. This method will be removed in Shiro 2.0. | |
319 | */ | |
320 | @Deprecated | |
321 | protected Object getSalt(AuthenticationToken token) { | |
322 | 1 | return token.getPrincipal(); |
323 | } | |
324 | ||
325 | /** | |
326 | * Returns a {@link Hash Hash} instance representing the already-hashed AuthenticationInfo credentials stored in the system. | |
327 | * <p/> | |
328 | * This method reconstructs a {@link Hash Hash} instance based on a {@code info.getCredentials} call, | |
329 | * but it does <em>not</em> hash that value - it is expected that method call will return an already-hashed value. | |
330 | * <p/> | |
331 | * This implementation's reconstruction effort functions as follows: | |
332 | * <ol> | |
333 | * <li>Convert {@code account.getCredentials()} to a byte array via the {@link #toBytes toBytes} method. | |
334 | * <li>If {@code account.getCredentials()} was originally a String or char[] before {@code toBytes} was | |
335 | * called, check for encoding: | |
336 | * <li>If {@link #storedCredentialsHexEncoded storedCredentialsHexEncoded}, Hex decode that byte array, otherwise | |
337 | * Base64 decode the byte array</li> | |
338 | * <li>Set the byte[] array directly on the {@code Hash} implementation and return it.</li> | |
339 | * </ol> | |
340 | * | |
341 | * @param info the AuthenticationInfo from which to retrieve the credentials which assumed to be in already-hashed form. | |
342 | * @return a {@link Hash Hash} instance representing the given AuthenticationInfo's stored credentials. | |
343 | */ | |
344 | protected Object getCredentials(AuthenticationInfo info) { | |
345 | 20 | Object credentials = info.getCredentials(); |
346 | ||
347 | 20 | byte[] storedBytes = toBytes(credentials); |
348 | ||
349 | 20 | if (credentials instanceof String || credentials instanceof char[]) { |
350 | //account.credentials were a char[] or String, so | |
351 | //we need to do text decoding first: | |
352 | 11 | if (isStoredCredentialsHexEncoded()) { |
353 | 11 | storedBytes = Hex.decode(storedBytes); |
354 | } else { | |
355 | 0 | storedBytes = Base64.decode(storedBytes); |
356 | } | |
357 | } | |
358 | 20 | AbstractHash hash = newHashInstance(); |
359 | 20 | hash.setBytes(storedBytes); |
360 | 20 | return hash; |
361 | } | |
362 | ||
363 | /** | |
364 | * This implementation first hashes the {@code token}'s credentials, potentially using a | |
365 | * {@code salt} if the {@code info} argument is a | |
366 | * {@link org.apache.shiro.authc.SaltedAuthenticationInfo SaltedAuthenticationInfo}. It then compares the hash | |
367 | * against the {@code AuthenticationInfo}'s | |
368 | * {@link #getCredentials(org.apache.shiro.authc.AuthenticationInfo) already-hashed credentials}. This method | |
369 | * returns {@code true} if those two values are {@link #equals(Object, Object) equal}, {@code false} otherwise. | |
370 | * | |
371 | * @param token the {@code AuthenticationToken} submitted during the authentication attempt. | |
372 | * @param info the {@code AuthenticationInfo} stored in the system matching the token principal | |
373 | * @return {@code true} if the provided token credentials hash match to the stored account credentials hash, | |
374 | * {@code false} otherwise | |
375 | * @since 1.1 | |
376 | */ | |
377 | @Override | |
378 | public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { | |
379 | 20 | Object tokenHashedCredentials = hashProvidedCredentials(token, info); |
380 | 20 | Object accountCredentials = getCredentials(info); |
381 | 20 | return equals(tokenHashedCredentials, accountCredentials); |
382 | } | |
383 | ||
384 | /** | |
385 | * Hash the provided {@code token}'s credentials using the salt stored with the account if the | |
386 | * {@code info} instance is an {@code instanceof} {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} (see | |
387 | * the class-level JavaDoc for why this is the preferred approach). | |
388 | * <p/> | |
389 | * If the {@code info} instance is <em>not</em> | |
390 | * an {@code instanceof} {@code SaltedAuthenticationInfo}, the logic will fall back to Shiro 1.0 | |
391 | * backwards-compatible logic: it will first check to see {@link #isHashSalted() isHashSalted} and if so, will try | |
392 | * to acquire the salt from {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)}. See the class-level | |
393 | * JavaDoc for why this is not recommended. This 'fallback' logic exists only for backwards-compatibility. | |
394 | * {@code Realm}s should be updated as soon as possible to return {@code SaltedAuthenticationInfo} instances | |
395 | * if account credentials salting is enabled (highly recommended for password-based systems). | |
396 | * | |
397 | * @param token the submitted authentication token from which its credentials will be hashed | |
398 | * @param info the stored account data, potentially used to acquire a salt | |
399 | * @return the token credentials hash | |
400 | * @since 1.1 | |
401 | */ | |
402 | protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) { | |
403 | 20 | Object salt = null; |
404 | 20 | if (info instanceof SaltedAuthenticationInfo) { |
405 | 18 | salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt(); |
406 | } else { | |
407 | //retain 1.0 backwards compatibility: | |
408 | 2 | if (isHashSalted()) { |
409 | 1 | salt = getSalt(token); |
410 | } | |
411 | } | |
412 | 20 | return hashProvidedCredentials(token.getCredentials(), salt, getHashIterations()); |
413 | } | |
414 | ||
415 | /** | |
416 | * Returns the {@link #getHashAlgorithmName() hashAlgorithmName} property, but will throw an | |
417 | * {@link IllegalStateException} if it has not been set. | |
418 | * | |
419 | * @return the required {@link #getHashAlgorithmName() hashAlgorithmName} property | |
420 | * @throws IllegalStateException if the property has not been set prior to calling this method. | |
421 | * @since 1.1 | |
422 | */ | |
423 | private String assertHashAlgorithmName() throws IllegalStateException { | |
424 | 40 | String hashAlgorithmName = getHashAlgorithmName(); |
425 | 40 | if (hashAlgorithmName == null) { |
426 | 0 | String msg = "Required 'hashAlgorithmName' property has not been set. This is required to execute " + |
427 | "the hashing algorithm."; | |
428 | 0 | throw new IllegalStateException(msg); |
429 | } | |
430 | 40 | return hashAlgorithmName; |
431 | } | |
432 | ||
433 | /** | |
434 | * Hashes the provided credentials a total of {@code hashIterations} times, using the given salt. The hash | |
435 | * implementation/algorithm used is based on the {@link #getHashAlgorithmName() hashAlgorithmName} property. | |
436 | * | |
437 | * @param credentials the submitted authentication token's credentials to hash | |
438 | * @param salt the value to salt the hash, or {@code null} if a salt will not be used. | |
439 | * @param hashIterations the number of times to hash the credentials. At least one hash will always occur though, | |
440 | * even if this argument is 0 or negative. | |
441 | * @return the hashed value of the provided credentials, according to the specified salt and hash iterations. | |
442 | */ | |
443 | protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) { | |
444 | 20 | String hashAlgorithmName = assertHashAlgorithmName(); |
445 | 20 | return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); |
446 | } | |
447 | ||
448 | /** | |
449 | * Returns a new, <em>uninitialized</em> instance, without its byte array set. Used as a utility method in the | |
450 | * {@link SimpleCredentialsMatcher#getCredentials(org.apache.shiro.authc.AuthenticationInfo) getCredentials(AuthenticationInfo)} implementation. | |
451 | * | |
452 | * @return a new, <em>uninitialized</em> instance, without its byte array set. | |
453 | */ | |
454 | protected AbstractHash newHashInstance() { | |
455 | 20 | String hashAlgorithmName = assertHashAlgorithmName(); |
456 | 20 | return new SimpleHash(hashAlgorithmName); |
457 | } | |
458 | ||
459 | } |