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     * &quot;Why add salt?&quot; 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    }