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