javaintermediate

Secure Password Hashing

Hash passwords securely with PBKDF2 and verify them — no external libraries required.

java
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import java.util.Base64;

public class PasswordHasher {
    private static final int ITERATIONS = 100_000;
    private static final int KEY_LENGTH = 256;
    private static final int SALT_LENGTH = 16;
    private static final String ALGORITHM = "PBKDF2WithHmacSHA256";

    // Hash a password → returns "iterations:salt:hash" (all Base64)
    public static String hash(String password) {
        byte[] salt = new byte[SALT_LENGTH];
        new SecureRandom().nextBytes(salt);

        byte[] hash = pbkdf2(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);

        return ITERATIONS + ":" +
            Base64.getEncoder().encodeToString(salt) + ":" +
            Base64.getEncoder().encodeToString(hash);
    }

    // Verify password against stored hash
    public static boolean verify(String password, String stored) {
        String[] parts = stored.split(":");
        if (parts.length != 3) return false;

        int iterations = Integer.parseInt(parts[0]);
        byte[] salt = Base64.getDecoder().decode(parts[1]);
        byte[] expectedHash = Base64.getDecoder().decode(parts[2]);

        byte[] actualHash = pbkdf2(password.toCharArray(), salt, iterations, expectedHash.length * 8);

        return slowEquals(expectedHash, actualHash);
    }

    // Constant-time comparison (prevents timing attacks)
    private static boolean slowEquals(byte[] a, byte[] b) {
        if (a.length != b.length) return false;
        int diff = 0;
        for (int i = 0; i < a.length; i++) {
            diff |= a[i] ^ b[i];
        }
        return diff == 0;
    }

    private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int keyLength) {
        try {
            PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength);
            SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM);
            return factory.generateSecret(spec).getEncoded();
        } catch (Exception e) {
            throw new RuntimeException("PBKDF2 failed", e);
        }
    }

    public static void main(String[] args) {
        String password = "mySecurePass123";

        // Hash
        String hashed = hash(password);
        System.out.println("Stored: " + hashed);

        // Verify (correct)
        System.out.println("Valid: " + verify(password, hashed));    // true
        System.out.println("Wrong: " + verify("wrongPass", hashed)); // false

        // Each hash is unique (different salt)
        System.out.println(hash(password)); // different from above
    }
}

Use Cases

  • User registration password storage
  • Secure credential verification
  • Password-based key derivation

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.