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 →