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.crypto.hash;
020    
021    import org.apache.shiro.crypto.RandomNumberGenerator;
022    import org.apache.shiro.crypto.SecureRandomNumberGenerator;
023    import org.apache.shiro.util.ByteSource;
024    
025    /**
026     * Default implementation of the {@link HashService} interface, supporting a customizable hash algorithm name,
027     * secure-random salt generation, multiple hash iterations and an optional internal
028     * {@link #setPrivateSalt(ByteSource) privateSalt}.
029     * <h2>Hash Algorithm</h2>
030     * You may specify a hash algorithm via the {@link #setHashAlgorithmName(String)} property.  Any algorithm name
031     * understood by the JDK
032     * {@link java.security.MessageDigest#getInstance(String) MessageDigest.getInstance(String algorithmName)} method
033     * will work.  The default is {@code SHA-512}.
034     * <h2>Random Salts</h2>
035     * When a salt is not specified in a request, this implementation generates secure random salts via its
036     * {@link #setRandomNumberGenerator(org.apache.shiro.crypto.RandomNumberGenerator) randomNumberGenerator} property.
037     * Random salts (and potentially combined with the internal {@link #getPrivateSalt() privateSalt}) is a very strong
038     * salting strategy, as salts should ideally never be based on known/guessable data.  The default instance is a
039     * {@link SecureRandomNumberGenerator}.
040     * <h2>Hash Iterations</h2>
041     * Secure hashing strategies often employ multiple hash iterations to slow down the hashing process.  This technique
042     * is usually used for password hashing, since the longer it takes to compute a password hash, the longer it would
043     * take for an attacker to compromise a password.  This
044     * <a href="http://www.katasoft.com/blog/2011/04/04/strong-password-hashing-apache-shiro">Katasoft blog article</a>
045     * explains in greater detail why this is useful, as well as information on how many iterations is 'enough'.
046     * <p/>
047     * You may set the number of hash iterations via the {@link #setHashIterations(int)} property.  The default is
048     * {@code 1}, but should be increased significantly if the {@code HashService} is intended to be used for password
049     * hashing. See the linked blog article for more info.
050     * <h2>Private Salt</h2>
051     * If using this implementation as part of a password hashing strategy, it might be desirable to configure a
052     * {@link #setPrivateSalt(ByteSource) private salt}:
053     * <p/>
054     * A hash and the salt used to compute it are often stored together.  If an attacker is ever able to access
055     * the hash (e.g. during password cracking) and it has the full salt value, the attacker has all of the input necessary
056     * to try to brute-force crack the hash (source + complete salt).
057     * <p/>
058     * However, if part of the salt is not available to the attacker (because it is not stored with the hash), it is
059     * <em>much</em> harder to crack the hash value since the attacker does not have the complete inputs necessary.
060     * <p/>
061     * The {@link #getPrivateSalt() privateSalt} property exists to satisfy this private-and-not-shared part of the salt.
062     * If you configure this attribute, you can obtain this additional very important safety feature.
063     * <p/>
064     * <b>*</b>By default, the {@link #getPrivateSalt() privateSalt} is null, since a sensible default cannot be used that
065     * isn't easily compromised (because Shiro is an open-source project and any default could be easily seen and used).
066     *
067     * @since 1.2
068     */
069    public class DefaultHashService implements ConfigurableHashService {
070    
071        /**
072         * The RandomNumberGenerator to use to randomly generate the public part of the hash salt.
073         */
074        private RandomNumberGenerator rng;
075    
076        /**
077         * The MessageDigest name of the hash algorithm to use for computing hashes.
078         */
079        private String algorithmName;
080    
081        /**
082         * The 'private' part of the hash salt.
083         */
084        private ByteSource privateSalt;
085    
086        /**
087         * The number of hash iterations to perform when computing hashes.
088         */
089        private int iterations;
090    
091        /**
092         * Whether or not to generate public salts if a request does not provide one.
093         */
094        private boolean generatePublicSalt;
095    
096        /**
097         * Constructs a new {@code DefaultHashService} instance with the following defaults:
098         * <ul>
099         * <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            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 combine(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    }