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 27 import java.io.Serializable; 28 import java.security.MessageDigest; 29 import java.security.NoSuchAlgorithmException; 30 import java.util.Arrays; 31 32 /** 33 * Provides a base for all Shiro Hash algorithms with support for salts and multiple hash iterations. 34 * <p/> 35 * Read 36 * <a href="http://www.owasp.org/index.php/Hashing_Java" target="blank">http://www.owasp.org/index.php/Hashing_Java</a> 37 * for a good article on the benefits of hashing, including what a 'salt' is as well as why it and multiple hash 38 * iterations can be useful. 39 * <p/> 40 * This class and its subclasses support hashing with additional capabilities of salting and multiple iterations via 41 * overloaded constructors. 42 * 43 * @since 0.9 44 * @deprecated in Shiro 1.1 in favor of using the concrete {@link SimpleHash} implementation directly. 45 */ 46 @Deprecated 47 public abstract class AbstractHash extends CodecSupport implements Hash, Serializable { 48 49 private static final long serialVersionUID = -4723044219611288405L; 50 /** 51 * The hashed data 52 */ 53 private byte[] bytes; 54 55 /** 56 * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead. 57 */ 58 private transient String hexEncoded; 59 /** 60 * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead. 61 */ 62 private transient String base64Encoded; 63 64 /** 65 * Creates an new instance without any of its properties set (no hashing is performed). 66 * <p/> 67 * Because all constructors in this class (except this one) hash the {@code source} constructor argument, this 68 * default, no-arg constructor is useful in scenarios when you have a byte array that you know is already hashed and 69 * just want to set the bytes in their raw form directly on an instance. After instantiating the instance with 70 * this default, no-arg constructor, you can then immediately call {@link #setBytes setBytes} to have a 71 * fully-initialized instance. 72 */ 73 public AbstractHash() { 74 } 75 76 /** 77 * Creates a hash of the specified {@code source} with no {@code salt} using a single hash iteration. 78 * <p/> 79 * It is a convenience constructor that merely executes <code>this( source, null, 1);</code>. 80 * <p/> 81 * Please see the 82 * {@link #AbstractHash(Object source, Object salt, int numIterations) AbstractHash(Object,Object,int)} 83 * constructor for the types of Objects that may be passed into this constructor, as well as how to support further 84 * types. 85 * 86 * @param source the object to be hashed. 87 * @throws CodecException if the specified {@code source} cannot be converted into a byte array (byte[]). 88 */ 89 public AbstractHash(Object source) throws CodecException { 90 this(source, null, 1); 91 } 92 93 /** 94 * Creates a hash of the specified {@code source} using the given {@code salt} using a single hash iteration. 95 * <p/> 96 * It is a convenience constructor that merely executes <code>this( source, salt, 1);</code>. 97 * <p/> 98 * Please see the 99 * {@link #AbstractHash(Object source, Object salt, int numIterations) AbstractHash(Object,Object,int)} 100 * constructor for the types of Objects that may be passed into this constructor, as well as how to support further 101 * types. 102 * 103 * @param source the source object to be hashed. 104 * @param salt the salt to use for the hash 105 * @throws CodecException if either constructor argument cannot be converted into a byte array. 106 */ 107 public AbstractHash(Object source, Object salt) throws CodecException { 108 this(source, salt, 1); 109 } 110 111 /** 112 * Creates a hash of the specified {@code source} using the given {@code salt} a total of 113 * {@code hashIterations} times. 114 * <p/> 115 * By default, this class only supports Object method arguments of 116 * type {@code byte[]}, {@code char[]}, {@link String}, {@link java.io.File File}, or 117 * {@link java.io.InputStream InputStream}. If either argument is anything other than these 118 * types a {@link org.apache.shiro.lang.codec.CodecException CodecException} will be thrown. 119 * <p/> 120 * If you want to be able to hash other object types, or use other salt types, you need to override the 121 * {@link #toBytes(Object) toBytes(Object)} method to support those specific types. Your other option is to 122 * convert your arguments to one of the default three supported types first before passing them in to this 123 * constructor}. 124 * 125 * @param source the source object to be hashed. 126 * @param salt the salt to use for the hash 127 * @param hashIterations the number of times the {@code source} argument hashed for attack resiliency. 128 * @throws CodecException if either Object constructor argument cannot be converted into a byte array. 129 */ 130 public AbstractHash(Object source, Object salt, int hashIterations) throws CodecException { 131 byte[] sourceBytes = toBytes(source); 132 byte[] saltBytes = null; 133 if (salt != null) { 134 saltBytes = toBytes(salt); 135 } 136 byte[] hashedBytes = hash(sourceBytes, saltBytes, hashIterations); 137 setBytes(hashedBytes); 138 } 139 140 /** 141 * Implemented by subclasses, this specifies the {@link MessageDigest MessageDigest} algorithm name 142 * to use when performing the hash. 143 * 144 * @return the {@link MessageDigest MessageDigest} algorithm name to use when performing the hash. 145 */ 146 @Override 147 public abstract String getAlgorithmName(); 148 149 @Override 150 public byte[] getBytes() { 151 return this.bytes; 152 } 153 154 /** 155 * Sets the raw bytes stored by this hash instance. 156 * <p/> 157 * The bytes are kept in raw form - they will not be hashed/changed. This is primarily a utility method for 158 * constructing a Hash instance when the hashed value is already known. 159 * 160 * @param alreadyHashedBytes the raw already-hashed bytes to store in this instance. 161 */ 162 public void setBytes(byte[] alreadyHashedBytes) { 163 this.bytes = alreadyHashedBytes; 164 this.hexEncoded = null; 165 this.base64Encoded = null; 166 } 167 168 /** 169 * Returns the JDK MessageDigest instance to use for executing the hash. 170 * 171 * @param algorithmName the algorithm to use for the hash, provided by subclasses. 172 * @return the MessageDigest object for the specified {@code algorithm}. 173 * @throws UnknownAlgorithmException if the specified algorithm name is not available. 174 */ 175 protected MessageDigest getDigest(String algorithmName) throws UnknownAlgorithmException { 176 try { 177 return MessageDigest.getInstance(algorithmName); 178 } catch (NoSuchAlgorithmException e) { 179 String msg = "No native '" + algorithmName + "' MessageDigest instance available on the current JVM."; 180 throw new UnknownAlgorithmException(msg, e); 181 } 182 } 183 184 /** 185 * Hashes the specified byte array without a salt for a single iteration. 186 * 187 * @param bytes the bytes to hash. 188 * @return the hashed bytes. 189 */ 190 protected byte[] hash(byte[] bytes) { 191 return hash(bytes, null, 1); 192 } 193 194 /** 195 * Hashes the specified byte array using the given {@code salt} for a single iteration. 196 * 197 * @param bytes the bytes to hash 198 * @param salt the salt to use for the initial hash 199 * @return the hashed bytes 200 */ 201 protected byte[] hash(byte[] bytes, byte[] salt) { 202 return hash(bytes, salt, 1); 203 } 204 205 /** 206 * Hashes the specified byte array using the given {@code salt} for the specified number of iterations. 207 * 208 * @param bytes the bytes to hash 209 * @param salt the salt to use for the initial hash 210 * @param hashIterations the number of times the the {@code bytes} will be hashed (for attack resiliency). 211 * @return the hashed bytes. 212 * @throws UnknownAlgorithmException if the {@link #getAlgorithmName() algorithmName} is not available. 213 */ 214 protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException { 215 MessageDigest digest = getDigest(getAlgorithmName()); 216 if (salt != null) { 217 digest.reset(); 218 digest.update(salt); 219 } 220 byte[] hashed = digest.digest(bytes); 221 //already hashed once above 222 int iterations = hashIterations - 1; 223 //iterate remaining number: 224 for (int i = 0; i < iterations; i++) { 225 digest.reset(); 226 hashed = digest.digest(hashed); 227 } 228 return hashed; 229 } 230 231 /** 232 * Returns a hex-encoded string of the underlying {@link #getBytes byte array}. 233 * <p/> 234 * This implementation caches the resulting hex string so multiple calls to this method remain efficient. 235 * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the 236 * next time this method is called. 237 * 238 * @return a hex-encoded string of the underlying {@link #getBytes byte array}. 239 */ 240 @Override 241 public String toHex() { 242 if (this.hexEncoded == null) { 243 this.hexEncoded = Hex.encodeToString(getBytes()); 244 } 245 return this.hexEncoded; 246 } 247 248 /** 249 * Returns a Base64-encoded string of the underlying {@link #getBytes byte array}. 250 * <p/> 251 * This implementation caches the resulting Base64 string so multiple calls to this method remain efficient. 252 * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the 253 * next time this method is called. 254 * 255 * @return a Base64-encoded string of the underlying {@link #getBytes byte array}. 256 */ 257 @Override 258 public String toBase64() { 259 if (this.base64Encoded == null) { 260 //cache result in case this method is called multiple times. 261 this.base64Encoded = Base64.encodeToString(getBytes()); 262 } 263 return this.base64Encoded; 264 } 265 266 /** 267 * Simple implementation that merely returns {@link #toHex() toHex()}. 268 * 269 * @return the {@link #toHex() toHex()} value. 270 */ 271 @Override 272 public String toString() { 273 return toHex(); 274 } 275 276 /** 277 * Returns {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to 278 * this Hash's byte array, {@code false} otherwise. 279 * 280 * @param o the object (Hash) to check for equality. 281 * @return {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to 282 * this Hash's byte array, {@code false} otherwise. 283 */ 284 @Override 285 public boolean equals(Object o) { 286 if (o instanceof Hash) { 287 Hash other = (Hash) o; 288 return MessageDigest.isEqual(getBytes(), other.getBytes()); 289 } 290 return false; 291 } 292 293 /** 294 * Simply returns toHex().hashCode(); 295 * 296 * @return toHex().hashCode() 297 */ 298 @Override 299 public int hashCode() { 300 if (this.bytes == null || this.bytes.length == 0) { 301 return 0; 302 } 303 return Arrays.hashCode(this.bytes); 304 } 305 306 }