Coverage Report - org.apache.shiro.authc.credential.HashedCredentialsMatcher
 
Classes in this File Line Coverage Branch Coverage Complexity
HashedCredentialsMatcher
81%
43/53
68%
11/16
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  
  * &quot;Why add salt?&quot; 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  
  * &quot;Hardening against the attacker's attack&quot;</a> section to learn more about why you might want to use
 107  
  * multiple hash iterations.
 108  
  * <h2>MD5 &amp; 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  
 }