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.UnknownAlgorithmException; 022import org.apache.shiro.lang.codec.Base64; 023import org.apache.shiro.lang.codec.CodecException; 024import org.apache.shiro.lang.codec.CodecSupport; 025import org.apache.shiro.lang.codec.Hex; 026 027import java.io.Serializable; 028import java.security.MessageDigest; 029import java.security.NoSuchAlgorithmException; 030import java.util.Arrays; 031 032/** 033 * Provides a base for all Shiro Hash algorithms with support for salts and multiple hash iterations. 034 * <p/> 035 * Read 036 * <a href="http://www.owasp.org/index.php/Hashing_Java" target="blank">http://www.owasp.org/index.php/Hashing_Java</a> 037 * for a good article on the benefits of hashing, including what a 'salt' is as well as why it and multiple hash 038 * iterations can be useful. 039 * <p/> 040 * This class and its subclasses support hashing with additional capabilities of salting and multiple iterations via 041 * overloaded constructors. 042 * 043 * @since 0.9 044 * @deprecated in Shiro 1.1 in favor of using the concrete {@link SimpleHash} implementation directly. 045 */ 046@Deprecated 047public abstract class AbstractHash extends CodecSupport implements Hash, Serializable { 048 049 private static final long serialVersionUID = -4723044219611288405L; 050 /** 051 * The hashed data 052 */ 053 private byte[] bytes; 054 055 /** 056 * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead. 057 */ 058 private transient String hexEncoded; 059 /** 060 * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead. 061 */ 062 private transient String base64Encoded; 063 064 /** 065 * Creates an new instance without any of its properties set (no hashing is performed). 066 * <p/> 067 * Because all constructors in this class (except this one) hash the {@code source} constructor argument, this 068 * default, no-arg constructor is useful in scenarios when you have a byte array that you know is already hashed and 069 * just want to set the bytes in their raw form directly on an instance. After instantiating the instance with 070 * this default, no-arg constructor, you can then immediately call {@link #setBytes setBytes} to have a 071 * fully-initialized instance. 072 */ 073 public AbstractHash() { 074 } 075 076 /** 077 * Creates a hash of the specified {@code source} with no {@code salt} using a single hash iteration. 078 * <p/> 079 * It is a convenience constructor that merely executes <code>this( source, null, 1);</code>. 080 * <p/> 081 * Please see the 082 * {@link #AbstractHash(Object source, Object salt, int numIterations) AbstractHash(Object,Object,int)} 083 * constructor for the types of Objects that may be passed into this constructor, as well as how to support further 084 * types. 085 * 086 * @param source the object to be hashed. 087 * @throws CodecException if the specified {@code source} cannot be converted into a byte array (byte[]). 088 */ 089 public AbstractHash(Object source) throws CodecException { 090 this(source, null, 1); 091 } 092 093 /** 094 * Creates a hash of the specified {@code source} using the given {@code salt} using a single hash iteration. 095 * <p/> 096 * It is a convenience constructor that merely executes <code>this( source, salt, 1);</code>. 097 * <p/> 098 * Please see the 099 * {@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}