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 */
019package org.apache.shiro.crypto.hash;
020
021import org.apache.shiro.crypto.RandomNumberGenerator;
022import org.apache.shiro.crypto.SecureRandomNumberGenerator;
023import 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 */
069public 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}