package com.hwlcn.security.crypto.hash;
import com.hwlcn.security.codec.Base64;
import com.hwlcn.security.codec.CodecException;
import com.hwlcn.security.codec.CodecSupport;
import com.hwlcn.security.codec.Hex;
import com.hwlcn.security.crypto.UnknownAlgorithmException;
import com.hwlcn.security.util.ByteSource;
import com.hwlcn.security.util.StringUtils;
import java.io.Serializable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class SimpleHash extends CodecSupport implements Hash, Serializable {
private static final int DEFAULT_ITERATIONS = 1;
private final String algorithmName;
private byte[] bytes;
private ByteSource salt;
private int iterations;
private transient String hexEncoded = null;
private transient String base64Encoded = null;
public SimpleHash(String algorithmName) {
this.algorithmName = algorithmName;
this.iterations = DEFAULT_ITERATIONS;
}
public SimpleHash(String algorithmName, Object source) throws CodecException, UnknownAlgorithmException {
this(algorithmName, source, null, DEFAULT_ITERATIONS);
}
public SimpleHash(String algorithmName, Object source, Object salt) throws CodecException, UnknownAlgorithmException {
this(algorithmName, source, salt, DEFAULT_ITERATIONS);
}
public SimpleHash(String algorithmName, Object source, Object salt, int hashIterations)
throws CodecException, UnknownAlgorithmException {
if (!StringUtils.hasText(algorithmName)) {
throw new NullPointerException("algorithmName argument cannot be null or empty.");
}
this.algorithmName = algorithmName;
this.iterations = Math.max(DEFAULT_ITERATIONS, hashIterations);
ByteSource saltBytes = null;
if (salt != null) {
saltBytes = convertSaltToBytes(salt);
this.salt = saltBytes;
}
ByteSource sourceBytes = convertSourceToBytes(source);
hash(sourceBytes, saltBytes, hashIterations);
}
protected ByteSource convertSourceToBytes(Object source) {
return toByteSource(source);
}
protected ByteSource convertSaltToBytes(Object salt) {
return toByteSource(salt);
}
protected ByteSource toByteSource(Object o) {
if (o == null) {
return null;
}
if (o instanceof ByteSource) {
return (ByteSource) o;
}
byte[] bytes = toBytes(o);
return ByteSource.Util.bytes(bytes);
}
private void hash(ByteSource source, ByteSource salt, int hashIterations) throws CodecException, UnknownAlgorithmException {
byte[] saltBytes = salt != null ? salt.getBytes() : null;
byte[] hashedBytes = hash(source.getBytes(), saltBytes, hashIterations);
setBytes(hashedBytes);
}
public String getAlgorithmName() {
return this.algorithmName;
}
public ByteSource getSalt() {
return this.salt;
}
public int getIterations() {
return this.iterations;
}
public byte[] getBytes() {
return this.bytes;
}
public void setBytes(byte[] alreadyHashedBytes) {
this.bytes = alreadyHashedBytes;
this.hexEncoded = null;
this.base64Encoded = null;
}
public void setIterations(int iterations) {
this.iterations = Math.max(DEFAULT_ITERATIONS, iterations);
}
public void setSalt(ByteSource salt) {
this.salt = salt;
}
protected MessageDigest getDigest(String algorithmName) throws UnknownAlgorithmException {
try {
return MessageDigest.getInstance(algorithmName);
} catch (NoSuchAlgorithmException e) {
String msg = "No native '" + algorithmName + "' MessageDigest instance available on the current JVM.";
throw new UnknownAlgorithmException(msg, e);
}
}
protected byte[] hash(byte[] bytes) throws UnknownAlgorithmException {
return hash(bytes, null, DEFAULT_ITERATIONS);
}
protected byte[] hash(byte[] bytes, byte[] salt) throws UnknownAlgorithmException {
return hash(bytes, salt, DEFAULT_ITERATIONS);
}
protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException {
MessageDigest digest = getDigest(getAlgorithmName());
if (salt != null) {
digest.reset();
digest.update(salt);
}
byte[] hashed = digest.digest(bytes);
int iterations = hashIterations - DEFAULT_ITERATIONS;
for (int i = 0; i < iterations; i++) {
digest.reset();
hashed = digest.digest(hashed);
}
return hashed;
}
public boolean isEmpty() {
return this.bytes == null || this.bytes.length == 0;
}
public String toHex() {
if (this.hexEncoded == null) {
this.hexEncoded = Hex.encodeToString(getBytes());
}
return this.hexEncoded;
}
public String toBase64() {
if (this.base64Encoded == null) {
this.base64Encoded = Base64.encodeToString(getBytes());
}
return this.base64Encoded;
}
public String toString() {
return toHex();
}
public boolean equals(Object o) {
if (o instanceof Hash) {
Hash other = (Hash) o;
return Arrays.equals(getBytes(), other.getBytes());
}
return false;
}
public int hashCode() {
if (this.bytes == null || this.bytes.length == 0) {
return 0;
}
return Arrays.hashCode(this.bytes);
}
}