Sorcerer's IsleCode cfPassphrase / files

  1package crackstation.PBKDF2;
  2
  3import java.security.SecureRandom;
  4import javax.crypto.spec.PBEKeySpec;
  5import javax.crypto.SecretKeyFactory;
  6import java.math.BigInteger;
  7import java.security.NoSuchAlgorithmException;
  8import java.security.spec.InvalidKeySpecException;
  9
 10/*
 11 * PBKDF2 salted password hashing.
 12 * Author: havoc AT defuse.ca
 13 * www: http://crackstation.net/hashing-security.htm
 14 *
 15 * Note:
 16 * Modified version with additional createHash methods
 17 * to receive arguments Iterations,SaltBytes,HashBytes;
 18 */
 19public class PasswordHash
 20{
 21    public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";
 22
 23    // The following constants may be changed without breaking existing hashes.
 24    public static final int SALT_BYTES = 24;
 25    public static final int HASH_BYTES = 24;
 26    public static final int PBKDF2_ITERATIONS = 86000;
 27
 28    public static final int ITERATION_INDEX = 0;
 29    public static final int SALT_INDEX = 1;
 30    public static final int PBKDF2_INDEX = 2;
 31
 32    /**
 33     * Returns a salted PBKDF2 hash of the password.
 34     *
 35     * @param   password    the password to hash
 36     * @return              a salted PBKDF2 hash of the password
 37     */
 38    public static String createHash(String password)
 39        throws NoSuchAlgorithmException, InvalidKeySpecException
 40    {
 41        return createHash(password.toCharArray(),null,null,null);
 42    }
 43
 44    /**
 45     * Returns a salted PBKDF2 hash of the password.
 46     *
 47     * @param   password    the password to hash
 48     * @param   Iterations  the number of hash iterations. Default 1000
 49     * @param   SaltBytes   the size of the salt. Default 24 bytes.
 50     * @param   HashBytes   the size of the hash. Default 24 bytes.
 51     * @return              a salted PBKDF2 hash of the password
 52     */
 53    public static String createHash
 54    	( String  password
 55    	, Integer Iterations
 56    	, Integer SaltBytes
 57    	, Integer HashBytes
 58    	)
 59        throws NoSuchAlgorithmException, InvalidKeySpecException
 60    {
 61        return createHash(password.toCharArray(),Iterations,SaltBytes,HashBytes);
 62    }
 63
 64    /**
 65     * Returns a salted PBKDF2 hash of the password.
 66     *
 67     * @param   password    the password to hash
 68     * @return              a salted PBKDF2 hash of the password
 69     */
 70    public static String createHash(char[] password)
 71        throws NoSuchAlgorithmException, InvalidKeySpecException
 72    {
 73        return createHash(password,null,null,null);
 74    }
 75
 76    /**
 77     * Returns a salted PBKDF2 hash of the password.
 78     *
 79     * @param   password    the password to hash
 80     * @param   Iterations  the number of hash iterations. Default 1000
 81     * @param   SaltBytes   the size of the salt. Default 24 bytes.
 82     * @param   HashBytes   the size of the hash. Default 24 bytes.
 83     * @return              a salted PBKDF2 hash of the password
 84     */
 85    public static String createHash
 86    	( char[]  password
 87    	, Integer Iterations
 88    	, Integer SaltBytes
 89    	, Integer HashBytes
 90    	)
 91        throws NoSuchAlgorithmException, InvalidKeySpecException
 92    {
 93    	if (Iterations == null) Iterations = PBKDF2_ITERATIONS;
 94    	if (SaltBytes  == null) SaltBytes  = SALT_BYTES;
 95    	if (HashBytes  == null) HashBytes  = HASH_BYTES;
 96
 97        // Generate a random salt
 98        SecureRandom random = new SecureRandom();
 99        byte[] salt = new byte[SaltBytes];
100        random.nextBytes(salt);
101
102        // Hash the password
103        byte[] hash = pbkdf2(password, salt, Iterations, HashBytes);
104        // format iterations:salt:hash
105        return Iterations + ":" + toHex(salt) + ":" +  toHex(hash);
106    }
107
108    /**
109     * Validates a password using a hash.
110     *
111     * @param   password    the password to check
112     * @param   goodHash    the hash of the valid password
113     * @return              true if the password is correct, false if not
114     */
115    public static boolean validatePassword(String password, String goodHash)
116        throws NoSuchAlgorithmException, InvalidKeySpecException
117    {
118        return validatePassword(password.toCharArray(), goodHash);
119    }
120
121    /**
122     * Validates a password using a hash.
123     *
124     * @param   password    the password to check
125     * @param   goodHash    the hash of the valid password
126     * @return              true if the password is correct, false if not
127     */
128    public static boolean validatePassword(char[] password, String goodHash)
129        throws NoSuchAlgorithmException, InvalidKeySpecException
130    {
131        // Decode the hash into its parameters
132        String[] params = goodHash.split(":");
133        int iterations = Integer.parseInt(params[ITERATION_INDEX]);
134        byte[] salt = fromHex(params[SALT_INDEX]);
135        byte[] hash = fromHex(params[PBKDF2_INDEX]);
136        // Compute the hash of the provided password, using the same salt,
137        // iteration count, and hash length
138        byte[] testHash = pbkdf2(password, salt, iterations, hash.length);
139        // Compare the hashes in constant time. The password is correct if
140        // both hashes match.
141        return slowEquals(hash, testHash);
142    }
143
144    /**
145     * Compares two byte arrays in length-constant time. This comparison method
146     * is used so that password hashes cannot be extracted from an on-line
147     * system using a timing attack and then attacked off-line.
148     *
149     * @param   a       the first byte array
150     * @param   b       the second byte array
151     * @return          true if both byte arrays are the same, false if not
152     */
153    private static boolean slowEquals(byte[] a, byte[] b)
154    {
155        int diff = a.length ^ b.length;
156        for(int i = 0; i < a.length && i < b.length; i++)
157            diff |= a[i] ^ b[i];
158        return diff == 0;
159    }
160
161    /**
162     *  Computes the PBKDF2 hash of a password.
163     *
164     * @param   password    the password to hash.
165     * @param   salt        the salt
166     * @param   iterations  the iteration count (slowness factor)
167     * @param   bytes       the length of the hash to compute in bytes
168     * @return              the PBDKF2 hash of the password
169     */
170    private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes)
171        throws NoSuchAlgorithmException, InvalidKeySpecException
172    {
173        PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);
174        SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
175        return skf.generateSecret(spec).getEncoded();
176    }
177
178    /**
179     * Converts a string of hexadecimal characters into a byte array.
180     *
181     * @param   hex         the hex string
182     * @return              the hex string decoded into a byte array
183     */
184    private static byte[] fromHex(String hex)
185    {
186        byte[] binary = new byte[hex.length() / 2];
187        for(int i = 0; i < binary.length; i++)
188        {
189            binary[i] = (byte)Integer.parseInt(hex.substring(2*i, 2*i+2), 16);
190        }
191        return binary;
192    }
193
194    /**
195     * Converts a byte array into a hexadecimal string.
196     *
197     * @param   array       the byte array to convert
198     * @return              a length*2 character string encoding the byte array
199     */
200    private static String toHex(byte[] array)
201    {
202        BigInteger bi = new BigInteger(1, array);
203        String hex = bi.toString(16);
204        int paddingLength = (array.length * 2) - hex.length();
205        if(paddingLength > 0)
206            return String.format("%0" + paddingLength + "d", 0) + hex;
207        else
208            return hex;
209    }
210
211}