001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.shiro.authc.credential;
020
021 import org.apache.shiro.authc.AuthenticationInfo;
022 import org.apache.shiro.authc.AuthenticationToken;
023 import org.apache.shiro.codec.Base64;
024 import org.apache.shiro.codec.Hex;
025 import org.apache.shiro.crypto.hash.AbstractHash;
026 import org.apache.shiro.crypto.hash.Hash;
027
028 /**
029 * A {@code HashedCredentialMatcher} provides support for hashing of supplied {@code AuthenticationToken} credentials
030 * before being compared to those in the {@code AuthenticationInfo} from the data store.
031 * <p/>
032 * Credential hashing is one of the most common security techniques when safeguarding a user's private credentials
033 * (passwords, keys, etc). Most developers never want to store their users' credentials in plain form, viewable by
034 * anyone, so they often hash the users' credentials before they are saved in the data store.
035 * <p/>
036 * This class (and its subclasses) function as follows:
037 * <p/>
038 * It first hashes the {@code AuthenticationToken} credentials supplied by the user during their login. It then
039 * compares this hashed value directly with the {@code AuthenticationInfo} credentials stored in the system. The stored account
040 * credentials are expected to already be in hashed form. If these two values are equal, the submitted credentials
041 * match.
042 * <h3>Salting and Multiple Hash Iterations</h3>
043 * Because simple hashing is sometimes not good enough for many applications, this class also supports 'salting'
044 * and multiple hash iterations. Please read this excellent
045 * <a href="http://www.owasp.org/index.php/Hashing_Java" _target="blank">Hashing Java article</a> to learn about
046 * salting and multiple iterations and why you might want to use them. (Note of sections 5
047 * "Why add salt?" and 6 "Hardening against the attacker's attack").
048 * <p/>
049 * We should also note here that all of Shiro's Hash implementations (for example,
050 * {@link org.apache.shiro.crypto.hash.Md5Hash Md5Hash}, {@link org.apache.shiro.crypto.hash.Sha1Hash Sha1Hash}, etc)
051 * support salting and multiple hash iterations via overloaded constructors.
052 * <h4>Salting</h4>
053 * Salting of the authentication token's credentials hash is disabled by default, but you may enable it by setting
054 * {@link #setHashSalted hashSalted} to
055 * {@code true}. If you do enable it, the value used to salt the hash will be
056 * obtained from {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getSalt(authenticationToken)}.
057 * <p/>
058 * The default {@code getSalt} implementation merely returns
059 * {@code token.getPrincipal()}, effectively using the user's identity as the salt, a most common
060 * technique. If you wish to provide the authentication token's salt another way, you may override this
061 * {@code getSalt} method.
062 * <h4>Multiple Hash Iterations</h4>
063 * If you hash your users' credentials multiple times before persisting to the data store, you will also need to
064 * set this class's {@link #setHashIterations(int) hashIterations} property.
065 * <p/>
066 * <b>Note:</b> <a href="http://en.wikipedia.org/wiki/MD5">MD5</a> and
067 * <a href="http://en.wikipedia.org/wiki/SHA_hash_functions">SHA-1</a> algorithms are now known to be vulnerable to
068 * compromise and/or collisions (read the linked pages for more). While most applications are ok with either of these
069 * two, if your application mandates high security, use the SHA-256 (or higher) hashing algorithms and their
070 * supporting {@code CredentialsMatcher} implementations.
071 *
072 * @author Les Hazlewood
073 * @see org.apache.shiro.crypto.hash.Md5Hash
074 * @see org.apache.shiro.crypto.hash.Sha1Hash
075 * @see org.apache.shiro.crypto.hash.Sha256Hash
076 * @since 0.9
077 */
078 public abstract class HashedCredentialsMatcher extends SimpleCredentialsMatcher {
079
080 private boolean storedCredentialsHexEncoded = true; //false means base64 encoded
081 private boolean hashSalted = false;
082 private int hashIterations = 1;
083
084 /**
085 * Returns {@code true} if the system's stored credential hash is Hex encoded, {@code false} if it
086 * is Base64 encoded.
087 * <p/>
088 * Default value is {@code true} for convenience - all of Shiro's {@link Hash Hash#toString()}
089 * implementations return Hex encoded values by default, making this class's use with those implementations
090 * easier.
091 *
092 * @return {@code true} if the system's stored credential hash is Hex encoded, {@code false} if it
093 * is Base64 encoded. Default is {@code true}
094 */
095 public boolean isStoredCredentialsHexEncoded() {
096 return storedCredentialsHexEncoded;
097 }
098
099 /**
100 * Sets the indicator if this system's stored credential hash is Hex encoded or not.
101 * <p/>
102 * A value of {@code true} will cause this class to decode the system credential from Hex, a
103 * value of {@code false} will cause this class to decode the system credential from Base64.
104 * <p/>
105 * Unless overridden via this method, the default value is {@code true} for convenience - all of Shiro's
106 * {@link Hash Hash#toString()} implementations return Hex encoded values by default, making this class's use with
107 * those implementations easier.
108 *
109 * @param storedCredentialsHexEncoded the indicator if this system's stored credential hash is Hex
110 * encoded or not ('not' automatically implying it is Base64 encoded).
111 */
112 public void setStoredCredentialsHexEncoded(boolean storedCredentialsHexEncoded) {
113 this.storedCredentialsHexEncoded = storedCredentialsHexEncoded;
114 }
115
116 /**
117 * Returns {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing,
118 * {@code false} if it should not be salted.
119 * <p/>
120 * If enabled, the salt used will be obtained via the {@link #getSalt(AuthenticationToken) getSalt} method.
121 * <p/>
122 * The default value is {@code false}.
123 *
124 * @return {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing,
125 * {@code false} if it should not be salted.
126 */
127 public boolean isHashSalted() {
128 return hashSalted;
129 }
130
131 /**
132 * Sets whether or not to salt a submitted {@code AuthenticationToken}'s credentials when hashing.
133 * <p/>
134 * If enabled, the salt used will be obtained via the {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getSalt} method.
135 * </p>
136 * The default value is {@code false}.
137 *
138 * @param hashSalted whether or not to salt a submitted {@code AuthenticationToken}'s credentials when hashing.
139 */
140 public void setHashSalted(boolean hashSalted) {
141 this.hashSalted = hashSalted;
142 }
143
144 /**
145 * Returns the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before
146 * comparing to the credentials stored in the system.
147 * <p/>
148 * Unless overridden, the default value is {@code 1}, meaning a normal hash execution will occur.
149 *
150 * @return the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before
151 * comparing to the credentials stored in the system.
152 */
153 public int getHashIterations() {
154 return hashIterations;
155 }
156
157 /**
158 * Sets the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before comparing
159 * to the credentials stored in the system.
160 * <p/>
161 * Unless overridden, the default value is {@code 1}, meaning a normal single hash execution will occur.
162 * <p/>
163 * If this argument is less than 1 (i.e. 0 or negative), the default value of 1 is applied. There must always be
164 * at least 1 hash iteration (otherwise there would be no hash).
165 *
166 * @param hashIterations the number of times to hash a submitted {@code AuthenticationToken}'s credentials.
167 */
168 public void setHashIterations(int hashIterations) {
169 if (hashIterations < 1) {
170 this.hashIterations = 1;
171 } else {
172 this.hashIterations = hashIterations;
173 }
174 }
175
176 /**
177 * Returns a salt value used to hash the token's credentials.
178 * <p/>
179 * This default implementation merely returns {@code token.getPrincipal()}, effectively using the user's
180 * identity (username, user id, etc) as the salt, a most common technique. If you wish to provide the
181 * authentication token's salt another way, you may override this method.
182 *
183 * @param token the AuthenticationToken submitted during the authentication attempt.
184 * @return a salt value to use to hash the authentication token's credentials.
185 */
186 protected Object getSalt(AuthenticationToken token) {
187 return token.getPrincipal();
188 }
189
190 /**
191 * As this is a HashedCredentialMatcher, this method overrides the parent method by returning a hashed value
192 * of the submitted token's credentials.
193 * <p/>
194 * Based on this class's configuration, the return value may be salted and/or
195 * hashed multiple times (see the class-level JavaDoc for more information on salting and
196 * multiple hash iterations).
197 *
198 * @param token the authentication token submitted during the authentication attempt.
199 * @return the hashed value of the authentication token's credentials.
200 */
201 protected Object getCredentials(AuthenticationToken token) {
202 Object credentials = token.getCredentials();
203 Object salt = isHashSalted() ? getSalt(token) : null;
204 return hashProvidedCredentials(credentials, salt, getHashIterations());
205 }
206
207 /**
208 * Returns a {@link Hash Hash} instance representing the already-hashed AuthenticationInfo credentials stored in the system.
209 * <p/>
210 * This method reconstructs a {@link Hash Hash} instance based on a {@code info.getCredentials} call,
211 * but it does <em>not</em> hash that value - it is expected that method call will return an already-hashed value.
212 * <p/>
213 * This implementation's reconstruction effort functions as follows:
214 * <ol>
215 * <li>Convert {@code account.getCredentials()} to a byte array via the {@link #toBytes toBytes} method.
216 * <li>If {@code account.getCredentials()} was originally a String or char[] before {@code toBytes} was
217 * called, check for encoding:
218 * <li>If {@link #storedCredentialsHexEncoded storedCredentialsHexEncoded}, Hex decode that byte array, otherwise
219 * Base64 decode the byte array</li>
220 * <li>Set the byte[] array directly on the {@code Hash} implementation and return it.</li>
221 * </ol>
222 *
223 * @param info the AuthenticationInfo from which to retrieve the credentials which assumed to be in already-hashed form.
224 * @return a {@link Hash Hash} instance representing the given AuthenticationInfo's stored credentials.
225 */
226 protected Object getCredentials(AuthenticationInfo info) {
227 Object credentials = info.getCredentials();
228
229 byte[] storedBytes = toBytes(credentials);
230
231 if (credentials instanceof String || credentials instanceof char[]) {
232 //account.credentials were a char[] or String, so
233 //we need to do text decoding first:
234 if (isStoredCredentialsHexEncoded()) {
235 storedBytes = Hex.decode(storedBytes);
236 } else {
237 storedBytes = Base64.decode(storedBytes);
238 }
239 }
240 AbstractHash hash = newHashInstance();
241 hash.setBytes(storedBytes);
242 return hash;
243 }
244
245 /**
246 * Hashes the provided credentials a total of {@code hashIterations} times, using the given salt. The hash
247 * implementation/algorithm used is left to subclasses.
248 *
249 * @param credentials the submitted authentication token's credentials to hash
250 * @param salt the value to salt the hash, or {@code null} if a salt will not be used.
251 * @param hashIterations the number of times to hash the credentials. At least one hash will always occur though,
252 * even if this argument is 0 or negative.
253 * @return the hashed value of the provided credentials, according to the specified salt and hash iterations.
254 */
255 protected abstract Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations);
256
257 /**
258 * Returns a new, <em>uninitialized</em> instance, without its byte array set. Used as a utility method in the
259 * {@link SimpleCredentialsMatcher#getCredentials(org.apache.shiro.authc.AuthenticationInfo) getCredentials(AuthenticationInfo)} implementation.
260 *
261 * @return a new, <em>uninitialized</em> instance, without its byte array set.
262 */
263 protected abstract AbstractHash newHashInstance();
264
265 }