packagecom.founder.mrp.util;/** Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
* Copyright (c) 2013, Taylor Hornby
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.*/
importjava.math.BigInteger;importjava.security.NoSuchAlgorithmException;importjava.security.SecureRandom;importjava.security.spec.InvalidKeySpecException;importjavax.crypto.SecretKeyFactory;importjavax.crypto.spec.PBEKeySpec;/** PBKDF2 salted password hashing.
* Author: havoc AT defuse.ca
* www:http://crackstation.net/hashing-security.htm
*/
public classPasswordHash
{public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";//The following constants may be changed without breaking existing hashes.
public static final int SALT_BYTE_SIZE = 24;public static final int HASH_BYTE_SIZE = 24;public static final int PBKDF2_ITERATIONS = 1000;public static final int ITERATION_INDEX = 0;public static final int SALT_INDEX = 1;public static final int PBKDF2_INDEX = 2;/*** Returns a salted PBKDF2 hash of the password.
*
*@parampassword the password to hash
*@returna salted PBKDF2 hash of the password*/
public staticString createHash(String password)throwsNoSuchAlgorithmException, InvalidKeySpecException
{returncreateHash(password.toCharArray());
}/*** Returns a salted PBKDF2 hash of the password.
*
*@parampassword the password to hash
*@returna salted PBKDF2 hash of the password*/
public static String createHash(char[] password)throwsNoSuchAlgorithmException, InvalidKeySpecException
{//Generate a random salt
SecureRandom random = newSecureRandom();byte[] salt = new byte[SALT_BYTE_SIZE];
random.nextBytes(salt);//Hash the password
byte[] hash =pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);//format iterations:salt:hash
return PBKDF2_ITERATIONS + ":" + toHex(salt) + ":" +toHex(hash);
}/*** Validates a password using a hash.
*
*@parampassword the password to check
*@paramcorrectHash the hash of the valid password
*@returntrue if the password is correct, false if not*/
public static booleanvalidatePassword(String password, String correctHash)throwsNoSuchAlgorithmException, InvalidKeySpecException
{returnvalidatePassword(password.toCharArray(), correctHash);
}/*** Validates a password using a hash.
*
*@parampassword the password to check
*@paramcorrectHash the hash of the valid password
*@returntrue if the password is correct, false if not*/
public static boolean validatePassword(char[] password, String correctHash)throwsNoSuchAlgorithmException, InvalidKeySpecException
{//Decode the hash into its parameters
String[] params = correctHash.split(":");int iterations =Integer.parseInt(params[ITERATION_INDEX]);byte[] salt =fromHex(params[SALT_INDEX]);byte[] hash =fromHex(params[PBKDF2_INDEX]);//Compute the hash of the provided password, using the same salt,//iteration count, and hash length
byte[] testHash =pbkdf2(password, salt, iterations, hash.length);//Compare the hashes in constant time. The password is correct if//both hashes match.
returnslowEquals(hash, testHash);
}/*** Compares two byte arrays in length-constant time. This comparison method
* is used so that password hashes cannot be extracted from an on-line
* system using a timing attack and then attacked off-line.
*
*@parama the first byte array
*@paramb the second byte array
*@returntrue if both byte arrays are the same, false if not*/
private static boolean slowEquals(byte[] a, byte[] b)
{int diff = a.length ^b.length;for(int i = 0; i < a.length && i < b.length; i++)
diff|= a[i] ^b[i];return diff == 0;
}/*** Computes the PBKDF2 hash of a password.
*
*@parampassword the password to hash.
*@paramsalt the salt
*@paramiterations the iteration count (slowness factor)
*@parambytes the length of the hash to compute in bytes
*@returnthe PBDKF2 hash of the password*/
private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, intbytes)throwsNoSuchAlgorithmException, InvalidKeySpecException
{
PBEKeySpec spec= new PBEKeySpec(password, salt, iterations, bytes * 8);
SecretKeyFactory skf=SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);returnskf.generateSecret(spec).getEncoded();
}/*** Converts a string of hexadecimal characters into a byte array.
*
*@paramhex the hex string
*@returnthe hex string decoded into a byte array*/
private static byte[] fromHex(String hex)
{byte[] binary = new byte[hex.length() / 2];for(int i = 0; i < binary.length; i++)
{
binary[i]= (byte)Integer.parseInt(hex.substring(2*i, 2*i+2), 16);
}returnbinary;
}/*** Converts a byte array into a hexadecimal string.
*
*@paramarray the byte array to convert
*@returna length*2 character string encoding the byte array*/
private static String toHex(byte[] array)
{
BigInteger bi= new BigInteger(1, array);
String hex= bi.toString(16);int paddingLength = (array.length * 2) -hex.length();if(paddingLength > 0)return String.format("%0" + paddingLength + "d", 0) +hex;else
returnhex;
}/*** Tests the basic functionality of the PasswordHash class
*
*@paramargs ignored*/
public static voidmain(String[] args)
{try{//Print out 10 hashes
for(int i = 0; i < 10; i++)
System.out.println(PasswordHash.createHash("123"));//Test password validation
boolean failure = false;
System.out.println("Running tests...");for(int i = 0; i < 100; i++)
{
String password= ""+i;
String hash=createHash(password);
String secondHash=createHash(password);if(hash.equals(secondHash)) {
System.out.println("FAILURE: TWO HASHES ARE EQUAL!");
failure= true;
}
String wrongPassword= ""+(i+1);if(validatePassword(wrongPassword, hash)) {
System.out.println("FAILURE: WRONG PASSWORD ACCEPTED!");
failure= true;
}if(!validatePassword(password, hash)) {
System.out.println("FAILURE: GOOD PASSWORD NOT ACCEPTED!");
failure= true;
}
}if(failure)
System.out.println("TESTS FAILED!");elseSystem.out.println("TESTS PASSED!");
}catch(Exception ex)
{
System.out.println("ERROR: " +ex);
}
}
}