- cfpassphrase/src/crackstation/PBKDF2/PasswordHash.java
- 7 KB
- 210
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}