1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.shiro.crypto.hash; 20 21 import org.apache.shiro.crypto.UnknownAlgorithmException; 22 import org.apache.shiro.lang.codec.Base64; 23 import org.apache.shiro.lang.codec.CodecException; 24 import org.apache.shiro.lang.codec.CodecSupport; 25 import org.apache.shiro.lang.codec.Hex; 26 import org.apache.shiro.lang.util.ByteSource; 27 import org.apache.shiro.lang.util.SimpleByteSource; 28 import org.apache.shiro.lang.util.StringUtils; 29 import org.slf4j.Logger; 30 import org.slf4j.LoggerFactory; 31 32 import java.io.Serializable; 33 import java.security.MessageDigest; 34 import java.security.NoSuchAlgorithmException; 35 import java.util.Arrays; 36 37 import static java.util.Objects.requireNonNull; 38 39 /** 40 * A {@code Hash} implementation that allows any {@link java.security.MessageDigest MessageDigest} algorithm name to 41 * be used. This class is a less type-safe variant than the other {@code AbstractHash} subclasses 42 * (e.g. {@link Sha512Hash}, etc.), but it does allow for any algorithm name to be specified in case the other subclass 43 * implementations do not represent an algorithm that you may want to use. 44 * <p/> 45 * As of Shiro 1.1, this class effectively replaces the (now-deprecated) {@code AbstractHash} class. It subclasses 46 * {@code AbstractHash} only to retain backwards-compatibility. 47 * 48 * @since 1.1 49 */ 50 public class SimpleHash extends CodecSupport implements Hash, Serializable { 51 52 private static final int DEFAULT_ITERATIONS = 1; 53 private static final long serialVersionUID = -6689895264902387303L; 54 55 private static final Logger LOG = LoggerFactory.getLogger(SimpleHash.class); 56 57 /** 58 * The {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. 59 */ 60 private final String algorithmName; 61 62 /** 63 * The hashed data 64 */ 65 private byte[] bytes; 66 67 /** 68 * Supplied salt, if any. 69 */ 70 private ByteSource salt; 71 72 /** 73 * Number of hash iterations to perform. Defaults to 1 in the constructor. 74 */ 75 private int iterations; 76 77 /** 78 * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead. 79 */ 80 private transient String hexEncoded; 81 82 /** 83 * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead. 84 */ 85 private transient String base64Encoded; 86 87 /** 88 * Creates an new instance with only its {@code algorithmName} set - no hashing is performed. 89 * <p/> 90 * Because all other constructors in this class hash the {@code source} constructor argument, this 91 * constructor is useful in scenarios when you have a byte array that you know is already hashed and 92 * just want to set the bytes in their raw form directly on an instance. After using this constructor, 93 * you can then immediately call {@link #setBytes setBytes} to have a fully-initialized instance. 94 * <p/> 95 * <b>N.B.</b>The algorithm identified by the {@code algorithmName} parameter must be available on the JVM. If it 96 * is not, a {@link UnknownAlgorithmException} will be thrown when the hash is performed (not at instantiation). 97 * 98 * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when 99 * performing the hash. 100 * @see UnknownAlgorithmException 101 */ 102 public SimpleHash(String algorithmName) { 103 this.algorithmName = algorithmName; 104 this.iterations = DEFAULT_ITERATIONS; 105 } 106 107 /** 108 * Creates an {@code algorithmName}-specific hash of the specified {@code source} with no {@code salt} using a 109 * single hash iteration. 110 * <p/> 111 * This is a convenience constructor that merely executes <code>this( algorithmName, source, null, 1);</code>. 112 * <p/> 113 * Please see the 114 * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) 115 * SimpleHashHash(algorithmName, Object,Object,int)} 116 * constructor for the types of Objects that may be passed into this constructor, as well as how to support further 117 * types. 118 * 119 * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when 120 * performing the hash. 121 * @param source the object to be hashed. 122 * @throws org.apache.shiro.lang.codec.CodecException if the specified {@code source} cannot be converted 123 * into a byte array (byte[]). 124 * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. 125 */ 126 public SimpleHash(String algorithmName, Object source) throws CodecException, UnknownAlgorithmException { 127 this(algorithmName, source, SimpleByteSource.empty(), DEFAULT_ITERATIONS); 128 } 129 130 /** 131 * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given {@code salt} 132 * using a single hash iteration. 133 * <p/> 134 * It is a convenience constructor that merely executes <code>this( algorithmName, source, salt, 1);</code>. 135 * <p/> 136 * Please see the 137 * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) 138 * SimpleHashHash(algorithmName, Object,Object,int)} 139 * constructor for the types of Objects that may be passed into this constructor, as well as how to support further 140 * types. 141 * 142 * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when 143 * performing the hash. 144 * @param source the source object to be hashed. 145 * @param salt the salt to use for the hash 146 * @throws CodecException if either constructor argument cannot be converted into a byte array. 147 * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. 148 */ 149 public SimpleHash(String algorithmName, Object source, Object salt) throws CodecException, UnknownAlgorithmException { 150 this(algorithmName, source, salt, DEFAULT_ITERATIONS); 151 } 152 153 /** 154 * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given {@code salt} 155 * using a single hash iteration. 156 * <p/> 157 * It is a convenience constructor that merely executes <code>this( algorithmName, source, salt, 1);</code>. 158 * <p/> 159 * Please see the 160 * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) 161 * SimpleHashHash(algorithmName, Object,Object,int)} 162 * constructor for the types of Objects that may be passed into this constructor, as well as how to support further 163 * types. 164 * 165 * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when 166 * performing the hash. 167 * @param source the source object to be hashed. 168 * @param hashIterations the number of times the {@code source} argument hashed for attack resiliency. 169 * @throws CodecException if either constructor argument cannot be converted into a byte array. 170 * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. 171 */ 172 public SimpleHash(String algorithmName, Object source, int hashIterations) throws CodecException, UnknownAlgorithmException { 173 this(algorithmName, source, SimpleByteSource.empty(), hashIterations); 174 } 175 176 /** 177 * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given 178 * {@code salt} a total of {@code hashIterations} times. 179 * <p/> 180 * By default, this class only supports Object method arguments of 181 * type {@code byte[]}, {@code char[]}, {@link String}, {@link java.io.File File}, 182 * {@link java.io.InputStream InputStream} or {@link org.apache.shiro.lang.util.ByteSource ByteSource}. If either 183 * argument is anything other than these types a {@link org.apache.shiro.lang.codec.CodecException CodecException} 184 * will be thrown. 185 * <p/> 186 * If you want to be able to hash other object types, or use other salt types, you need to override the 187 * {@link #toBytes(Object) toBytes(Object)} method to support those specific types. Your other option is to 188 * convert your arguments to one of the default supported types first before passing them in to this 189 * constructor}. 190 * 191 * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when 192 * performing the hash. 193 * @param source the source object to be hashed. 194 * @param salt the salt to use for the hash 195 * @param hashIterations the number of times the {@code source} argument hashed for attack resiliency. 196 * @throws CodecException if either Object constructor argument cannot be converted into a byte array. 197 * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. 198 */ 199 public SimpleHash(String algorithmName, Object source, Object salt, int hashIterations) 200 throws CodecException, UnknownAlgorithmException { 201 if (!StringUtils.hasText(algorithmName)) { 202 throw new NullPointerException("algorithmName argument cannot be null or empty."); 203 } 204 this.algorithmName = algorithmName; 205 this.iterations = Math.max(DEFAULT_ITERATIONS, hashIterations); 206 ByteSource saltBytes = convertSaltToBytes(salt); 207 this.salt = saltBytes; 208 ByteSource sourceBytes = convertSourceToBytes(source); 209 hash(sourceBytes, saltBytes, hashIterations); 210 } 211 212 /** 213 * Acquires the specified {@code source} argument's bytes and returns them in the form of a {@code ByteSource} instance. 214 * <p/> 215 * This implementation merely delegates to the convenience {@link #toByteSource(Object)} method for generic 216 * conversion. Can be overridden by subclasses for source-specific conversion. 217 * 218 * @param source the source object to be hashed. 219 * @return the source's bytes in the form of a {@code ByteSource} instance. 220 * @since 1.2 221 */ 222 protected ByteSource convertSourceToBytes(Object source) { 223 return toByteSource(source); 224 } 225 226 /** 227 * Acquires the specified {@code salt} argument's bytes and returns them in the form of a {@code ByteSource} instance. 228 * <p/> 229 * This implementation merely delegates to the convenience {@link #toByteSource(Object)} method for generic 230 * conversion. Can be overridden by subclasses for salt-specific conversion. 231 * 232 * @param salt the salt to be use for the hash. 233 * @return the salt's bytes in the form of a {@code ByteSource} instance. 234 * @since 1.2 235 */ 236 protected ByteSource convertSaltToBytes(Object salt) { 237 return toByteSource(salt); 238 } 239 240 /** 241 * Converts a given object into a {@code ByteSource} instance. Assumes the object can be converted to bytes. 242 * 243 * @param object the Object to convert into a {@code ByteSource} instance. 244 * @return the {@code ByteSource} representation of the specified object's bytes. 245 * @since 1.2 246 */ 247 protected ByteSource toByteSource(Object object) { 248 if (object instanceof ByteSource) { 249 return (ByteSource) object; 250 } 251 byte[] bytes = toBytes(object); 252 return ByteSource.Util.bytes(bytes); 253 } 254 255 private void hash(ByteSource source, ByteSource salt, int hashIterations) throws CodecException, UnknownAlgorithmException { 256 byte[] saltBytes = requireNonNull(salt).getBytes(); 257 byte[] hashedBytes = hash(source.getBytes(), saltBytes, hashIterations); 258 setBytes(hashedBytes); 259 } 260 261 /** 262 * Returns the {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. 263 * 264 * @return the {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. 265 */ 266 @Override 267 public String getAlgorithmName() { 268 return this.algorithmName; 269 } 270 271 @Override 272 public ByteSource getSalt() { 273 return this.salt; 274 } 275 276 @Override 277 public int getIterations() { 278 return this.iterations; 279 } 280 281 @Override 282 public boolean matchesPassword(ByteSource plaintextBytes) { 283 try { 284 SimpleHash otherHash = new SimpleHash(this.getAlgorithmName(), plaintextBytes, this.getSalt(), this.getIterations()); 285 return this.equals(otherHash); 286 } catch (IllegalArgumentException illegalArgumentException) { 287 // cannot recreate hash. Do not log password. 288 LOG.warn("Cannot recreate a hash using the same parameters.", illegalArgumentException); 289 return false; 290 } 291 } 292 293 @Override 294 public byte[] getBytes() { 295 return this.bytes; 296 } 297 298 /** 299 * Sets the raw bytes stored by this hash instance. 300 * <p/> 301 * The bytes are kept in raw form - they will not be hashed/changed. This is primarily a utility method for 302 * constructing a Hash instance when the hashed value is already known. 303 * 304 * @param alreadyHashedBytes the raw already-hashed bytes to store in this instance. 305 */ 306 public void setBytes(byte[] alreadyHashedBytes) { 307 this.bytes = alreadyHashedBytes; 308 this.hexEncoded = null; 309 this.base64Encoded = null; 310 } 311 312 /** 313 * Sets the iterations used to previously compute AN ALREADY GENERATED HASH. 314 * <p/> 315 * This is provided <em>ONLY</em> to reconstitute an already-created Hash instance. It should ONLY ever be 316 * invoked when re-constructing a hash instance from an already-hashed value. 317 * 318 * @param iterations the number of hash iterations used to previously create the hash/digest. 319 * @since 1.2 320 */ 321 public void setIterations(int iterations) { 322 this.iterations = Math.max(DEFAULT_ITERATIONS, iterations); 323 } 324 325 /** 326 * Sets the salt used to previously compute AN ALREADY GENERATED HASH. 327 * <p/> 328 * This is provided <em>ONLY</em> to reconstitute a Hash instance that has already been computed. It should ONLY 329 * ever be invoked when re-constructing a hash instance from an already-hashed value. 330 * 331 * @param salt the salt used to previously create the hash/digest. 332 * @since 1.2 333 */ 334 public void setSalt(ByteSource salt) { 335 this.salt = salt; 336 } 337 338 /** 339 * Returns the JDK MessageDigest instance to use for executing the hash. 340 * 341 * @param algorithmName the algorithm to use for the hash, provided by subclasses. 342 * @return the MessageDigest object for the specified {@code algorithm}. 343 * @throws UnknownAlgorithmException if the specified algorithm name is not available. 344 */ 345 protected MessageDigest getDigest(String algorithmName) throws UnknownAlgorithmException { 346 try { 347 return MessageDigest.getInstance(algorithmName); 348 } catch (NoSuchAlgorithmException e) { 349 String msg = "No native '" + algorithmName + "' MessageDigest instance available on the current JVM."; 350 throw new UnknownAlgorithmException(msg, e); 351 } 352 } 353 354 /** 355 * Hashes the specified byte array without a salt for a single iteration. 356 * 357 * @param bytes the bytes to hash. 358 * @return the hashed bytes. 359 * @throws UnknownAlgorithmException if the configured {@link #getAlgorithmName() algorithmName} is not available. 360 */ 361 protected byte[] hash(byte[] bytes) throws UnknownAlgorithmException { 362 return hash(bytes, new byte[0], DEFAULT_ITERATIONS); 363 } 364 365 /** 366 * Hashes the specified byte array using the given {@code salt} for a single iteration. 367 * 368 * @param bytes the bytes to hash 369 * @param salt the salt to use for the initial hash 370 * @return the hashed bytes 371 * @throws UnknownAlgorithmException if the configured {@link #getAlgorithmName() algorithmName} is not available. 372 */ 373 protected byte[] hash(byte[] bytes, byte[] salt) throws UnknownAlgorithmException { 374 return hash(bytes, salt, DEFAULT_ITERATIONS); 375 } 376 377 /** 378 * Hashes the specified byte array using the given {@code salt} for the specified number of iterations. 379 * 380 * @param bytes the bytes to hash 381 * @param salt the salt to use for the initial hash 382 * @param hashIterations the number of times the the {@code bytes} will be hashed (for attack resiliency). 383 * @return the hashed bytes. 384 * @throws UnknownAlgorithmException if the {@link #getAlgorithmName() algorithmName} is not available. 385 */ 386 protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException { 387 MessageDigest digest = getDigest(getAlgorithmName()); 388 if (salt.length != 0) { 389 digest.reset(); 390 digest.update(salt); 391 } 392 byte[] hashed = digest.digest(bytes); 393 //already hashed once above 394 int iterations = hashIterations - 1; 395 //iterate remaining number: 396 for (int i = 0; i < iterations; i++) { 397 digest.reset(); 398 hashed = digest.digest(hashed); 399 } 400 return hashed; 401 } 402 403 @Override 404 public boolean isEmpty() { 405 return this.bytes == null || this.bytes.length == 0; 406 } 407 408 /** 409 * Returns a hex-encoded string of the underlying {@link #getBytes byte array}. 410 * <p/> 411 * This implementation caches the resulting hex string so multiple calls to this method remain efficient. 412 * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the 413 * next time this method is called. 414 * 415 * @return a hex-encoded string of the underlying {@link #getBytes byte array}. 416 */ 417 @Override 418 public String toHex() { 419 if (this.hexEncoded == null) { 420 this.hexEncoded = Hex.encodeToString(getBytes()); 421 } 422 return this.hexEncoded; 423 } 424 425 /** 426 * Returns a Base64-encoded string of the underlying {@link #getBytes byte array}. 427 * <p/> 428 * This implementation caches the resulting Base64 string so multiple calls to this method remain efficient. 429 * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the 430 * next time this method is called. 431 * 432 * @return a Base64-encoded string of the underlying {@link #getBytes byte array}. 433 */ 434 @Override 435 public String toBase64() { 436 if (this.base64Encoded == null) { 437 //cache result in case this method is called multiple times. 438 this.base64Encoded = Base64.encodeToString(getBytes()); 439 } 440 return this.base64Encoded; 441 } 442 443 /** 444 * Simple implementation that merely returns {@link #toHex() toHex()}. 445 * 446 * @return the {@link #toHex() toHex()} value. 447 */ 448 @Override 449 public String toString() { 450 return toHex(); 451 } 452 453 /** 454 * Returns {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to 455 * this Hash's byte array, {@code false} otherwise. 456 * 457 * @param o the object (Hash) to check for equality. 458 * @return {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to 459 * this Hash's byte array, {@code false} otherwise. 460 */ 461 @Override 462 public boolean equals(Object o) { 463 if (o instanceof Hash) { 464 Hash other = (Hash) o; 465 return MessageDigest.isEqual(getBytes(), other.getBytes()); 466 } 467 return false; 468 } 469 470 /** 471 * Simply returns toHex().hashCode(); 472 * 473 * @return toHex().hashCode() 474 */ 475 @Override 476 public int hashCode() { 477 if (this.bytes == null || this.bytes.length == 0) { 478 return 0; 479 } 480 return Arrays.hashCode(this.bytes); 481 } 482 }