Skip to content

Instantly share code, notes, and snippets.

@jsfeng
Created May 7, 2012 18:33
Show Gist options
  • Save jsfeng/2629511 to your computer and use it in GitHub Desktop.
Save jsfeng/2629511 to your computer and use it in GitHub Desktop.
Simple Hash Utility Tool
import java.util.Random;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import org.apache.commons.codec.binary.Base64;
public class SimpleHashUtil {
/**
* Generates a hash for the given plain text value and returns a
* base64-encoded result. Before the hash is computed, a random salt is
* generated and appended to the plain text. This salt is stored at the end
* of the hash value, so it can be used later for hash verification.
*
* @param plainText
* Plaintext value to be hashed. The function does not check
* whether this parameter is null.
* @param hashAlgorithm
* Name of the hash algorithm. Allowed values are: "MD5", "SHA1",
* "SHA256", "SHA384", and "SHA512" (if any other value is
* specified MD5 hashing algorithm will be used). This value is
* case-insensitive.
* @param saltBytes
* this parameter can be null, in which case a random salt value
* will be generated.
* @return hash value formatted as a base64-encoded String.
*/
public static String computeHash(String plainText, String hashAlgorithm,
byte[] saltBytes) {
try {
// If salt is not specified, generate it on the fly.
if (saltBytes == null) {
// Define min and max salt sizes.
int minSaltSize = 4;
int maxSaltSize = 8;
// Generate a random number for the size of the salt.
Random random = new Random();
int saltSize = random.nextInt(maxSaltSize - minSaltSize +1) + minSaltSize;
// Allocate a byte array, which will hold the salt.
saltBytes = new byte[saltSize];
// Initialize a random number generator.
SecureRandom sr;
try {
sr = SecureRandom.getInstance("SHA1PRNG");
// Fill the salt with cryptographically strong byte values.
sr.nextBytes(saltBytes);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// Convert plain text into a byte array.
byte[] plainTextBytes= {};
try {
plainTextBytes = plainText.getBytes("UTF8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Allocate array, which will hold plain text and salt.
byte[] plainTextWithSaltBytes = new byte[plainTextBytes.length
+ saltBytes.length];
// Copy plain text bytes into resulting array.
for (int i = 0; i < plainTextBytes.length; i++)
plainTextWithSaltBytes[i] = plainTextBytes[i];
// Append salt bytes to the resulting array.
for (int i = 0; i < saltBytes.length; i++)
plainTextWithSaltBytes[plainTextBytes.length + i] = saltBytes[i];
// Because we support multiple hashing algorithms, we must define
// hash object as a common (abstract) base class. We will specify the
// actual hashing algorithm class later during object creation.
MessageDigest md = null;
// Make sure hashing algorithm name is specified.
if (hashAlgorithm == null)
hashAlgorithm = "";
hashAlgorithm = hashAlgorithm.toUpperCase();
if(hashAlgorithm.equals("SHA1")){
md = MessageDigest.getInstance("SHA-1");
}else if(hashAlgorithm.equals("SHA256")){
md = MessageDigest.getInstance("SHA-256");
}else if(hashAlgorithm.equals("SHA384")){
md = MessageDigest.getInstance("SHA-384");
}else if(hashAlgorithm.equals("SHA512")){
md = MessageDigest.getInstance("SHA-512");
}else{
md = MessageDigest.getInstance("MD5");
}
md.update(plainTextWithSaltBytes);
// Compute hash value of our plain text with appended salt.
byte[] hashBytes = md.digest();
// Create array which will hold hash and original salt bytes.
byte[] hashWithSaltBytes = new byte[hashBytes.length + saltBytes.length];
// Copy hash bytes into resulting array.
for (int i = 0; i < hashBytes.length; i++)
hashWithSaltBytes[i] = hashBytes[i];
// Append salt bytes to the result.
for (int i = 0; i < saltBytes.length; i++)
hashWithSaltBytes[hashBytes.length + i] = saltBytes[i];
// Convert result into a base64-encoded String.
String hashValue = Base64.encodeBase64String(hashWithSaltBytes);
// Return the result.
return hashValue;
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* Compares a hash of the specified plain text value to a given hash value.
* Plain text is hashed with the same salt value as the original hash.
*
* @param plainText
* Plain text to be verified against the specified hash. The
* function does not check whether this parameter is null.
* @param hashAlgorithm
* Name of the hash algorithm. Allowed values are: "MD5", "SHA1",
* "SHA256", "SHA384", and "SHA512" (if any other value is
* specified, MD5 hashing algorithm will be used). This value is
* case-insensitive.
* @param hashValue
* Base64-encoded hash value produced by ComputeHash function.
* This value includes the original salt appended to it.
* @return If computed hash mathes the specified hash the function the
* return value is true; otherwise, the function returns false.
*/
public static boolean verifyHash(String plainText, String hashAlgorithm,
String hashValue) {
// Convert base64-encoded hash value into a byte array.
byte[] hashWithSaltBytes = {};
hashWithSaltBytes = Base64.decodeBase64(hashValue);
// We must know size of hash (without salt).
int hashSizeInBits, hashSizeInBytes;
// Make sure that hashing algorithm name is specified.
if (hashAlgorithm == null)
hashAlgorithm = "";
hashAlgorithm = hashAlgorithm.toUpperCase();
// Size of hash is based on the specified algorithm.
if(hashAlgorithm.equals("SHA1")){
hashSizeInBits = 160;
}else if(hashAlgorithm.equals("SHA256")){
hashSizeInBits = 256;
}else if(hashAlgorithm.equals("SHA384")){
hashSizeInBits = 384;
}else if(hashAlgorithm.equals("SHA512")){
hashSizeInBits = 512;
}else{
hashSizeInBits = 128;
}
// Convert size of hash from bits to bytes.
hashSizeInBytes = hashSizeInBits / 8;
// Make sure that the specified hash value is long enough.
if (hashWithSaltBytes.length < hashSizeInBytes)
return false;
// Allocate array to hold original salt bytes retrieved from hash.
byte[] saltBytes = new byte[hashWithSaltBytes.length - hashSizeInBytes];
// Copy salt from the end of the hash to the new array.
for (int i = 0; i < saltBytes.length; i++)
saltBytes[i] = hashWithSaltBytes[hashSizeInBytes + i];
// Compute a new hash String.
String expectedHashString = computeHash(plainText, hashAlgorithm,
saltBytes);
// If the computed hash matches the specified hash, the plain text value must be correct.
return (hashValue.equals(expectedHashString));
}
public static void main(String args[]) {
String hashString = SimpleHashUtil.computeHash("abcdefghijklmn", "SHA512", null);
System.out.println("hashString: " + hashString);
boolean verifyResult = SimpleHashUtil.verifyHash("abcdefghijklmn", "SHA512", hashString);
System.out.println("verifyResult: " + verifyResult);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment