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.stormpath.com/blog/strong-password-hashing-apache-shiro">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}