blob: 9ff05411e10b55efd181c097a19ff187c23b0aca [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../../jacoco-resources/report.gif" type="image/gif"/><title>DefaultHashService.java</title><link rel="stylesheet" href="../../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../../index.html" class="el_report">Apache Shiro :: Test Coverage</a> &gt; <a href="../index.html" class="el_bundle">shiro-crypto-hash</a> &gt; <a href="index.source.html" class="el_package">org.apache.shiro.crypto.hash</a> &gt; <span class="el_source">DefaultHashService.java</span></div><h1>DefaultHashService.java</h1><pre class="source lang-java linenums">/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* &quot;License&quot;); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.shiro.crypto.hash;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.util.ByteSource;
/**
* Default implementation of the {@link HashService} interface, supporting a customizable hash algorithm name,
* secure-random salt generation, multiple hash iterations and an optional internal
* {@link #setPrivateSalt(ByteSource) privateSalt}.
* &lt;h2&gt;Hash Algorithm&lt;/h2&gt;
* You may specify a hash algorithm via the {@link #setHashAlgorithmName(String)} property. Any algorithm name
* understood by the JDK
* {@link java.security.MessageDigest#getInstance(String) MessageDigest.getInstance(String algorithmName)} method
* will work. The default is {@code SHA-512}.
* &lt;h2&gt;Random Salts&lt;/h2&gt;
* When a salt is not specified in a request, this implementation generates secure random salts via its
* {@link #setRandomNumberGenerator(org.apache.shiro.crypto.RandomNumberGenerator) randomNumberGenerator} property.
* Random salts (and potentially combined with the internal {@link #getPrivateSalt() privateSalt}) is a very strong
* salting strategy, as salts should ideally never be based on known/guessable data. The default instance is a
* {@link SecureRandomNumberGenerator}.
* &lt;h2&gt;Hash Iterations&lt;/h2&gt;
* Secure hashing strategies often employ multiple hash iterations to slow down the hashing process. This technique
* is usually used for password hashing, since the longer it takes to compute a password hash, the longer it would
* take for an attacker to compromise a password. This
* &lt;a href=&quot;http://www.stormpath.com/blog/strong-password-hashing-apache-shiro&quot;&gt;blog article&lt;/a&gt;
* explains in greater detail why this is useful, as well as information on how many iterations is 'enough'.
* &lt;p/&gt;
* You may set the number of hash iterations via the {@link #setHashIterations(int)} property. The default is
* {@code 1}, but should be increased significantly if the {@code HashService} is intended to be used for password
* hashing. See the linked blog article for more info.
* &lt;h2&gt;Private Salt&lt;/h2&gt;
* If using this implementation as part of a password hashing strategy, it might be desirable to configure a
* {@link #setPrivateSalt(ByteSource) private salt}:
* &lt;p/&gt;
* A hash and the salt used to compute it are often stored together. If an attacker is ever able to access
* the hash (e.g. during password cracking) and it has the full salt value, the attacker has all of the input necessary
* to try to brute-force crack the hash (source + complete salt).
* &lt;p/&gt;
* However, if part of the salt is not available to the attacker (because it is not stored with the hash), it is
* &lt;em&gt;much&lt;/em&gt; harder to crack the hash value since the attacker does not have the complete inputs necessary.
* &lt;p/&gt;
* The {@link #getPrivateSalt() privateSalt} property exists to satisfy this private-and-not-shared part of the salt.
* If you configure this attribute, you can obtain this additional very important safety feature.
* &lt;p/&gt;
* &lt;b&gt;*&lt;/b&gt;By default, the {@link #getPrivateSalt() privateSalt} is null, since a sensible default cannot be used that
* isn't easily compromised (because Shiro is an open-source project and any default could be easily seen and used).
*
* @since 1.2
*/
<span class="pc bpc" id="L69" title="1 of 2 branches missed.">public class DefaultHashService implements ConfigurableHashService {</span>
/**
* The RandomNumberGenerator to use to randomly generate the public part of the hash salt.
*/
private RandomNumberGenerator rng;
/**
* The MessageDigest name of the hash algorithm to use for computing hashes.
*/
private String algorithmName;
/**
* The 'private' part of the hash salt.
*/
private ByteSource privateSalt;
/**
* The number of hash iterations to perform when computing hashes.
*/
private int iterations;
/**
* Whether or not to generate public salts if a request does not provide one.
*/
private boolean generatePublicSalt;
/**
* Constructs a new {@code DefaultHashService} instance with the following defaults:
* &lt;ul&gt;
* &lt;li&gt;{@link #setHashAlgorithmName(String) hashAlgorithmName} = {@code SHA-512}&lt;/li&gt;
* &lt;li&gt;{@link #setHashIterations(int) hashIterations} = {@code 1}&lt;/li&gt;
* &lt;li&gt;{@link #setRandomNumberGenerator(org.apache.shiro.crypto.RandomNumberGenerator) randomNumberGenerator} =
* new {@link SecureRandomNumberGenerator}()&lt;/li&gt;
* &lt;li&gt;{@link #setGeneratePublicSalt(boolean) generatePublicSalt} = {@code false}&lt;/li&gt;
* &lt;/ul&gt;
* &lt;p/&gt;
* If this hashService will be used for password hashing it is recommended to set the
* {@link #setPrivateSalt(ByteSource) privateSalt} and significantly increase the number of
* {@link #setHashIterations(int) hashIterations}. See the class-level JavaDoc for more information.
*/
<span class="fc" id="L110"> public DefaultHashService() {</span>
<span class="fc" id="L111"> this.algorithmName = &quot;SHA-512&quot;;</span>
<span class="fc" id="L112"> this.iterations = 1;</span>
<span class="fc" id="L113"> this.generatePublicSalt = false;</span>
<span class="fc" id="L114"> this.rng = new SecureRandomNumberGenerator();</span>
<span class="fc" id="L115"> }</span>
/**
* Computes and responds with a hash based on the specified request.
* &lt;p/&gt;
* This implementation functions as follows:
* &lt;ul&gt;
* &lt;li&gt;If the request's {@link org.apache.shiro.crypto.hash.HashRequest#getSalt() salt} is null:
* &lt;p/&gt;
* A salt will be generated and used to compute the hash. The salt is generated as follows:
* &lt;ol&gt;
* &lt;li&gt;Use the {@link #getRandomNumberGenerator() randomNumberGenerator} to generate a new random number.&lt;/li&gt;
* &lt;li&gt;{@link #combine(ByteSource, ByteSource) combine} this random salt with any configured
* {@link #getPrivateSalt() privateSalt}
* &lt;/li&gt;
* &lt;li&gt;Use the combined value as the salt used during hash computation&lt;/li&gt;
* &lt;/ol&gt;
* &lt;/li&gt;
* &lt;li&gt;
* If the request salt is not null:
* &lt;p/&gt;
* This indicates that the hash computation is for comparison purposes (of a
* previously computed hash). The request salt will be {@link #combine(ByteSource, ByteSource) combined} with any
* configured {@link #getPrivateSalt() privateSalt} and used as the complete salt during hash computation.
* &lt;/li&gt;
* &lt;/ul&gt;
* &lt;p/&gt;
* The returned {@code Hash}'s {@link Hash#getSalt() salt} property
* will contain &lt;em&gt;only&lt;/em&gt; the 'public' part of the salt and &lt;em&gt;NOT&lt;/em&gt; the privateSalt. See the class-level
* JavaDoc explanation for more info.
*
* @param request the request to process
* @return the response containing the result of the hash computation, as well as any hash salt used that should be
* exposed to the caller.
*/
public Hash computeHash(HashRequest request) {
<span class="pc bpc" id="L151" title="1 of 6 branches missed."> if (request == null || request.getSource() == null || request.getSource().isEmpty()) {</span>
<span class="fc" id="L152"> return null;</span>
}
<span class="fc" id="L155"> String algorithmName = getAlgorithmName(request);</span>
<span class="fc" id="L156"> ByteSource source = request.getSource();</span>
<span class="fc" id="L157"> int iterations = getIterations(request);</span>
<span class="fc" id="L159"> ByteSource publicSalt = getPublicSalt(request);</span>
<span class="fc" id="L160"> ByteSource privateSalt = getPrivateSalt();</span>
<span class="fc" id="L161"> ByteSource salt = combine(privateSalt, publicSalt);</span>
<span class="fc" id="L163"> Hash computed = new SimpleHash(algorithmName, source, salt, iterations);</span>
<span class="fc" id="L165"> SimpleHash result = new SimpleHash(algorithmName);</span>
<span class="fc" id="L166"> result.setBytes(computed.getBytes());</span>
<span class="fc" id="L167"> result.setIterations(iterations);</span>
//Only expose the public salt - not the real/combined salt that might have been used:
<span class="fc" id="L169"> result.setSalt(publicSalt);</span>
<span class="fc" id="L171"> return result;</span>
}
protected String getAlgorithmName(HashRequest request) {
<span class="fc" id="L175"> String name = request.getAlgorithmName();</span>
<span class="fc bfc" id="L176" title="All 2 branches covered."> if (name == null) {</span>
<span class="fc" id="L177"> name = getHashAlgorithmName();</span>
}
<span class="fc" id="L179"> return name;</span>
}
protected int getIterations(HashRequest request) {
<span class="fc" id="L183"> int iterations = Math.max(0, request.getIterations());</span>
<span class="fc bfc" id="L184" title="All 2 branches covered."> if (iterations &lt; 1) {</span>
<span class="fc" id="L185"> iterations = Math.max(1, getHashIterations());</span>
}
<span class="fc" id="L187"> return iterations;</span>
}
/**
* Returns the public salt that should be used to compute a hash based on the specified request or
* {@code null} if no public salt should be used.
* &lt;p/&gt;
* This implementation functions as follows:
* &lt;ol&gt;
* &lt;li&gt;If the request salt is not null and non-empty, this will be used, return it.&lt;/li&gt;
* &lt;li&gt;If the request salt is null or empty:
* &lt;ol&gt;
* &lt;li&gt;If a private salt has been set &lt;em&gt;OR&lt;/em&gt; {@link #isGeneratePublicSalt()} is {@code true},
* auto generate a random public salt via the configured
* {@link #getRandomNumberGenerator() randomNumberGenerator}.&lt;/li&gt;
* &lt;li&gt;If a private salt has not been configured and {@link #isGeneratePublicSalt()} is {@code false},
* do nothing - return {@code null} to indicate a salt should not be used during hash computation.&lt;/li&gt;
* &lt;/ol&gt;
* &lt;/li&gt;
* &lt;/ol&gt;
*
* @param request request the request to process
* @return the public salt that should be used to compute a hash based on the specified request or
* {@code null} if no public salt should be used.
*/
protected ByteSource getPublicSalt(HashRequest request) {
<span class="fc" id="L214"> ByteSource publicSalt = request.getSalt();</span>
<span class="pc bpc" id="L216" title="1 of 4 branches missed."> if (publicSalt != null &amp;&amp; !publicSalt.isEmpty()) {</span>
//a public salt was explicitly requested to be used - go ahead and use it:
<span class="fc" id="L218"> return publicSalt;</span>
}
<span class="fc" id="L221"> publicSalt = null;</span>
//check to see if we need to generate one:
<span class="fc" id="L224"> ByteSource privateSalt = getPrivateSalt();</span>
<span class="pc bpc" id="L225" title="1 of 4 branches missed."> boolean privateSaltExists = privateSalt != null &amp;&amp; !privateSalt.isEmpty();</span>
//If a private salt exists, we must generate a public salt to protect the integrity of the private salt.
//Or generate it if the instance is explicitly configured to do so:
<span class="fc bfc" id="L229" title="All 4 branches covered."> if (privateSaltExists || isGeneratePublicSalt()) {</span>
<span class="fc" id="L230"> publicSalt = getRandomNumberGenerator().nextBytes();</span>
}
<span class="fc" id="L233"> return publicSalt;</span>
}
/**
* Combines the specified 'private' salt bytes with the specified additional extra bytes to use as the
* total salt during hash computation. {@code privateSaltBytes} will be {@code null} }if no private salt has been
* configured.
*
* @param privateSalt the (possibly {@code null}) 'private' salt to combine with the specified extra bytes
* @param publicSalt the extra bytes to use in addition to the given private salt.
* @return a combination of the specified private salt bytes and extra bytes that will be used as the total
* salt during hash computation.
*/
protected ByteSource combine(ByteSource privateSalt, ByteSource publicSalt) {
<span class="fc bfc" id="L248" title="All 2 branches covered."> byte[] privateSaltBytes = privateSalt != null ? privateSalt.getBytes() : null;</span>
<span class="fc bfc" id="L249" title="All 2 branches covered."> int privateSaltLength = privateSaltBytes != null ? privateSaltBytes.length : 0;</span>
<span class="fc bfc" id="L251" title="All 2 branches covered."> byte[] publicSaltBytes = publicSalt != null ? publicSalt.getBytes() : null;</span>
<span class="fc bfc" id="L252" title="All 2 branches covered."> int extraBytesLength = publicSaltBytes != null ? publicSaltBytes.length : 0;</span>
<span class="fc" id="L254"> int length = privateSaltLength + extraBytesLength;</span>
<span class="fc bfc" id="L256" title="All 2 branches covered."> if (length &lt;= 0) {</span>
<span class="fc" id="L257"> return null;</span>
}
<span class="fc" id="L260"> byte[] combined = new byte[length];</span>
<span class="fc" id="L262"> int i = 0;</span>
<span class="fc bfc" id="L263" title="All 2 branches covered."> for (int j = 0; j &lt; privateSaltLength; j++) {</span>
<span class="pc bpc" id="L264" title="2 of 4 branches missed."> assert privateSaltBytes != null;</span>
<span class="fc" id="L265"> combined[i++] = privateSaltBytes[j];</span>
}
<span class="fc bfc" id="L267" title="All 2 branches covered."> for (int j = 0; j &lt; extraBytesLength; j++) {</span>
<span class="pc bpc" id="L268" title="2 of 4 branches missed."> assert publicSaltBytes != null;</span>
<span class="fc" id="L269"> combined[i++] = publicSaltBytes[j];</span>
}
<span class="fc" id="L272"> return ByteSource.Util.bytes(combined);</span>
}
public void setHashAlgorithmName(String name) {
<span class="fc" id="L276"> this.algorithmName = name;</span>
<span class="fc" id="L277"> }</span>
public String getHashAlgorithmName() {
<span class="fc" id="L280"> return this.algorithmName;</span>
}
public void setPrivateSalt(ByteSource privateSalt) {
<span class="fc" id="L284"> this.privateSalt = privateSalt;</span>
<span class="fc" id="L285"> }</span>
public ByteSource getPrivateSalt() {
<span class="fc" id="L288"> return this.privateSalt;</span>
}
public void setHashIterations(int count) {
<span class="fc" id="L292"> this.iterations = count;</span>
<span class="fc" id="L293"> }</span>
public int getHashIterations() {
<span class="fc" id="L296"> return this.iterations;</span>
}
public void setRandomNumberGenerator(RandomNumberGenerator rng) {
<span class="fc" id="L300"> this.rng = rng;</span>
<span class="fc" id="L301"> }</span>
public RandomNumberGenerator getRandomNumberGenerator() {
<span class="fc" id="L304"> return this.rng;</span>
}
/**
* Returns {@code true} if a public salt should be randomly generated and used to compute a hash if a
* {@link HashRequest} does not specify a salt, {@code false} otherwise.
* &lt;p/&gt;
* The default value is {@code false} but should definitely be set to {@code true} if the
* {@code HashService} instance is being used for password hashing.
* &lt;p/&gt;
* &lt;b&gt;NOTE:&lt;/b&gt; this property only has an effect if a {@link #getPrivateSalt() privateSalt} is NOT configured. If a
* private salt has been configured and a request does not provide a salt, a random salt will always be generated
* to protect the integrity of the private salt (without a public salt, the private salt would be exposed as-is,
* which is undesirable).
*
* @return {@code true} if a public salt should be randomly generated and used to compute a hash if a
* {@link HashRequest} does not specify a salt, {@code false} otherwise.
*/
public boolean isGeneratePublicSalt() {
<span class="fc" id="L323"> return generatePublicSalt;</span>
}
/**
* Sets whether or not a public salt should be randomly generated and used to compute a hash if a
* {@link HashRequest} does not specify a salt.
* &lt;p/&gt;
* The default value is {@code false} but should definitely be set to {@code true} if the
* {@code HashService} instance is being used for password hashing.
* &lt;p/&gt;
* &lt;b&gt;NOTE:&lt;/b&gt; this property only has an effect if a {@link #getPrivateSalt() privateSalt} is NOT configured. If a
* private salt has been configured and a request does not provide a salt, a random salt will always be generated
* to protect the integrity of the private salt (without a public salt, the private salt would be exposed as-is,
* which is undesirable).
*
* @param generatePublicSalt whether or not a public salt should be randomly generated and used to compute a hash
* if a {@link HashRequest} does not specify a salt.
*/
public void setGeneratePublicSalt(boolean generatePublicSalt) {
<span class="fc" id="L342"> this.generatePublicSalt = generatePublicSalt;</span>
<span class="fc" id="L343"> }</span>
}
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.3.201901230119</span></div></body></html>