ExerciseWarm-up
Warm-up
Crack your own hashes
10 minjunior1–10 yrs
One concept, guided. Near-impossible to fail.
What this teaches
- An unsalted hash lets the table itself reveal shared passwords
- A known hash is a lookup key, not a puzzle
- Speed is the attacker's budget, and a KDF removes it
- SecureRandom, not Random, and MessageDigest.isEqual, not equals
Starter
Starter.java
import java.util.*;
/**
* Warm-up: be the attacker for ten minutes.
*
* Everything here is the JDK. PBKDF2 has been in java.base since Java 6, so
* the correct version needs no dependency — which is worth knowing before
* someone tells you a fast hash was the only option.
*/
public class Starter {
static String sha256(String value) throws Exception {
var md = java.security.MessageDigest.getInstance("SHA-256");
return HexFormat.of().formatHex(md.digest(value.getBytes("UTF-8")));
}
public static void main(String[] args) throws Exception {
// TODO 1: hash the same password for two different users. Predict
// whether the stored values differ, then look.
System.out.println("alice : " + sha256("hunter2"));
System.out.println("bob : " + sha256("hunter2"));
// TODO 2: search the web for the hash printed above. It is the
// SHA-256 of a well-known password and it is already indexed. Say
// what that means for a leaked table of bare hashes.
// TODO 3: measure. Loop for one second hashing "guess" + i and count
// how many you complete. That number is an attacker's rate per core —
// and a GPU does several orders of magnitude more.
// TODO 4: now write the slow version. The JDK gives you:
//
// var spec = new javax.crypto.spec.PBEKeySpec(password, salt, 210_000, 256);
// javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
// .generateSecret(spec).getEncoded();
//
// Measure the same one-second budget against it. Compare the two rates.
// TODO 5: generate two salts with java.security.SecureRandom and hash
// "hunter2" with each. Confirm the results differ, and say what that
// stops an attacker doing.
// TODO 6: now generate five salts with `new Random(System.currentTimeMillis())`
// in a loop and print them. Explain the result in one sentence.
// TODO 7: verify a login — re-hash the supplied password with the
// STORED salt and compare with MessageDigest.isEqual. Say why not
// Arrays.equals, and name two other things you would compare the same
// way.
}
}Run it locally:
cd exercises/java/authentication/password-storage/01-warmup
javac Starter.java -d /tmp/out && java -cp /tmp/out StarterDone when
- You produced two identical hashes from two users with one password
- You measured a guess rate for SHA-256 and for PBKDF2
- You showed two salts producing different hashes for the same password
- You can state what the salt fixes and what it does not