package com.hwlcn.security.authc.credential;
import com.hwlcn.security.authc.AuthenticationInfo;
import com.hwlcn.security.authc.AuthenticationToken;
import com.hwlcn.security.authc.SaltedAuthenticationInfo;
import com.hwlcn.security.codec.Base64;
import com.hwlcn.security.codec.CodecSupport;
import com.hwlcn.security.codec.Hex;
import com.hwlcn.security.crypto.hash.Hash;
import com.hwlcn.security.crypto.hash.SimpleHash;
import com.hwlcn.security.util.StringUtils;
public class HashedCredentialsMatcher extends SimpleCredentialsMatcher {
private String hashAlgorithm;
private int hashIterations;
private boolean hashSalted;
private boolean storedCredentialsHexEncoded;
public HashedCredentialsMatcher() {
this.hashAlgorithm = null;
this.hashSalted = false;
this.hashIterations = 1;
this.storedCredentialsHexEncoded = true; //false means Base64-encoded
}
public HashedCredentialsMatcher(String hashAlgorithmName) {
this();
if (!StringUtils.hasText(hashAlgorithmName)) {
throw new IllegalArgumentException("hashAlgorithmName cannot be null or empty.");
}
this.hashAlgorithm = hashAlgorithmName;
}
public String getHashAlgorithmName() {
return hashAlgorithm;
}
public void setHashAlgorithmName(String hashAlgorithmName) {
this.hashAlgorithm = hashAlgorithmName;
}
public boolean isStoredCredentialsHexEncoded() {
return storedCredentialsHexEncoded;
}
public void setStoredCredentialsHexEncoded(boolean storedCredentialsHexEncoded) {
this.storedCredentialsHexEncoded = storedCredentialsHexEncoded;
}
public int getHashIterations() {
return hashIterations;
}
public void setHashIterations(int hashIterations) {
if (hashIterations < 1) {
this.hashIterations = 1;
} else {
this.hashIterations = hashIterations;
}
}
protected Object getCredentials(AuthenticationInfo info) {
Object credentials = info.getCredentials();
byte[] storedBytes = toBytes(credentials);
if (credentials instanceof String || credentials instanceof char[]) {
if (isStoredCredentialsHexEncoded()) {
storedBytes = Hex.decode(storedBytes);
} else {
storedBytes = Base64.decode(storedBytes);
}
}
SimpleHash hash = newHashInstance();
hash.setBytes(storedBytes);
return hash;
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
Object tokenHashedCredentials = hashProvidedCredentials(token, info);
Object accountCredentials = getCredentials(info);
return equals(tokenHashedCredentials, accountCredentials);
}
protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) {
Object salt = null;
if (info instanceof SaltedAuthenticationInfo) {
salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
}
return hashProvidedCredentials(token.getCredentials(), salt, getHashIterations());
}
private String assertHashAlgorithmName() throws IllegalStateException {
String hashAlgorithmName = getHashAlgorithmName();
if (hashAlgorithmName == null) {
String msg = "Required 'hashAlgorithmName' property has not been set. This is required to execute " +
"the hashing algorithm.";
throw new IllegalStateException(msg);
}
return hashAlgorithmName;
}
protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) {
String hashAlgorithmName = assertHashAlgorithmName();
return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
}
protected SimpleHash newHashInstance() {
String hashAlgorithmName = assertHashAlgorithmName();
return new SimpleHash(hashAlgorithmName);
}
}