Skip to content

Password Strength Analyser — Java source

Estimate password strength with zxcvbn - realistic dictionary and pattern cracking with crack-time estimates and improvement suggestions. Runs entirely in your browser.

This is the Java implementation — the same logic the interactive tool runs, in a shareable, citable form.

// password-strength-analyser — Java port: password strength scoring & feedback. Language: Java (17+). Port of src/lib/password-strength.ts — same self-contained heuristic estimator as this dir's python.py (the live TS/Go twins wrap zxcvbn, whose ranking data is too large to vendor).
import java.util.Locale;
import java.util.Set;

/** Password strength scoring, ported from the canonical TS lib. */
public final class PasswordStrengthAnalyser {
    static final String[] LABELS = { "Very weak", "Weak", "Fair", "Good", "Strong" };
    static final double[] THRESHOLDS = { 28, 36, 60, 128 };
    /** Curated common-password blocklist — an exact (lowercased) match forces score 0. */
    static final Set<String> COMMON = Set.of(
            "1234", "12345", "123123", "123456", "12345678", "123456789", "1234567890",
            "000000", "111111", "654321", "666666", "abc123", "admin", "baseball", "batman",
            "dragon", "iloveyou", "letmein", "login", "master", "monkey", "passw0rd", "password",
            "password1", "princess", "qwerty", "root", "shadow", "superman", "sunshine",
            "trustno1", "welcome");
    /** Keyboard / numeric runs — any length-4 window inside costs 12 bits. */
    static final String[] SEQUENCES = { "qwertyuiop", "asdfghjkl", "zxcvbnm", "1234567890", "0987654321", "abcdefg", "gfedcba" };
    /** Strength breakdown — mirrors the TS PasswordAnalysis / Go pwstrength.Result. */
    public record Analysis(int score, String label, double entropy, String crackTime) {}

    /** Guess-pool size: 26 lower + 26 upper + 10 digit + 33 ASCII symbol + 128 other. */
    static int poolSize(String pw) {
        boolean lower = false, upper = false, digit = false, sym = false, other = false;
        for (int i = 0; i < pw.length(); i++) {
            char c = pw.charAt(i);
            if (c >= 128) other = true; else if (Character.isLowerCase(c)) lower = true;
            else if (Character.isUpperCase(c)) upper = true; else if (Character.isDigit(c)) digit = true; else sym = true;
        }
        return (lower ? 26 : 0) + (upper ? 26 : 0) + (digit ? 10 : 0) + (sym ? 33 : 0) + (other ? 128 : 0);
    }
    /** Does {@code lower} contain any length-4 window of {@code seq}? */
    static boolean containsSequence(String lower, String seq) {
        for (int i = 0; i + 4 <= seq.length(); i++)
            if (lower.contains(seq.substring(i, i + 4))) return true;
        return false;
    }
    /** Human crack time at 1e10 guesses/s — zxcvbn's offline-fast display. */
    static String crackTime(double entropy) {
        if (entropy <= 0) return "instant";
        double seconds = Math.pow(2, entropy) / 1e10;
        if (seconds < 1) return "instant";
        double div = 1; String name = "second";
        if (seconds >= 31_536_000) { div = 31_536_000; name = "year"; }
        else if (seconds >= 2_592_000) { div = 2_592_000; name = "month"; }
        else if (seconds >= 86_400) { div = 86_400; name = "day"; }
        else if (seconds >= 3_600) { div = 3_600; name = "hour"; }
        else if (seconds >= 60) { div = 60; name = "minute"; }
        double v = seconds / div;
        if (v >= 1e9) return "centuries";
        if (v >= 1e6) return String.format(Locale.ROOT, "%.0f million years", v / 1e6);
        if (v >= 1e3) return String.format(Locale.ROOT, "%.0f thousand years", v / 1e3);
        return String.format(Locale.ROOT, "%.0f %ss", v, name);
    }
    /** Scores 0-4. {@code userInputs} seeds the estimator with related tokens (a substring match weakens the score). */
    public static Analysis analyse(String password, String... userInputs) {
        int len = password.length(); int pool = poolSize(password);
        double lp = pool > 1 ? Math.log(pool) / Math.log(2) : 0;
        int unique = (int) password.chars().distinct().count();
        double entropy = pool > 1 ? Math.min(len * lp, (unique + (len - unique) * 0.25) * lp) : 0;
        String lower = password.toLowerCase(Locale.ROOT);
        if (COMMON.contains(lower)) entropy = 0; // blocklist hit — dictionary collapse
        else {
            for (String seq : SEQUENCES) if (containsSequence(lower, seq)) entropy -= 12;
            for (String ui : userInputs) {
                String l = ui.toLowerCase(Locale.ROOT);
                if (l.length() >= 3 && lower.contains(l)) entropy -= 10;
            }
            entropy = Math.max(0, entropy);
        }
        int score = len < 4 || entropy < THRESHOLDS[0] ? 0 : entropy < THRESHOLDS[1] ? 1
                : entropy < THRESHOLDS[2] ? 2 : entropy < THRESHOLDS[3] ? 3 : 4;
        return new Analysis(score, LABELS[score], entropy, crackTime(entropy));
    }
    public static void main(String[] args) {
        for (String pw : new String[] { "password", "12345678", "Tr0ub4dour&3", "correct horse battery staple", "u2#9Xq!Lp$7wZ" }) {
            Analysis r = analyse(pw);
            System.out.printf("%-26s %d/4 %-9s %6.1f bits  %s%n", pw, r.score(), r.label(), r.entropy(), r.crackTime());
        }
    }
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →