Password Breach Checker — Java source
Check if a password has appeared in known data breaches using k-anonymity. Only the first 5 characters of the SHA-1 hash are sent - your full password never leaves your browser.
This is the Java implementation — the same logic the interactive tool runs, in a shareable, citable form.
// Password breach lookup via Have I Been Pwned's Pwned Passwords API, using
// k-anonymity: only the first 5 characters of the SHA-1 hash ever leave the
// machine.
//
// Language: Java (17+, standard library only)
// Ported from src/lib/breach-checker.ts
// display source — part of CosmoDev's polyglot tool pages.
//
// Per the polyglot porting rules, this snippet carries the SHA-1 hashing and
// prefix-splitting logic only — the HTTP fetch against
// https://api.pwnedpasswords.com/range/{PREFIX} (free, no key) is transport,
// not pure logic, and is intentionally omitted. The range response is one
// "SUFFIX:COUNT" line per hash sharing the prefix (~800 candidates); the
// suffix match happens locally via parseRangeBody.
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Locale;
public final class BreachChecker {
/** The verdict of one lookup (count -1 = lookup failed). */
public record BreachResult(
boolean breached,
long count,
/** First 5 chars of the uppercase SHA-1 hex — the only part sent to the API. */
String hashPrefix,
/** Remaining 35 chars of the hash, matched locally against the response. */
String hashSuffix,
/** Set when the lookup failed; null otherwise. */
String error) {
}
public static final String HIBP_RANGE_URL = "https://api.pwnedpasswords.com/range/";
private BreachChecker() {
}
/** SHA-1 of a UTF-8 string as uppercase hex (the format HIBP expects). */
public static String sha1Hex(String input) throws Exception {
byte[] digest = MessageDigest.getInstance("SHA-1").digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder(digest.length * 2);
for (byte b : digest) {
sb.append(String.format("%02X", b));
}
return sb.toString().toUpperCase(Locale.ROOT);
}
/** The 5-char k-anonymity prefix and the 35-char suffix of a hash. */
public record HashParts(String prefix, String suffix) {
}
/** Split a 40-char uppercase hash into the 5-char prefix and the 35-char suffix. */
public static HashParts splitHash(String hash) {
String h = hash.toUpperCase(Locale.ROOT);
return new HashParts(h.substring(0, 5), h.substring(5));
}
/**
* Search an HIBP range response for a hash suffix and return its breach count.
* Never throws; returns 0 when the suffix is not present. Tolerates LF and
* CRLF line endings, blank lines, and leading/trailing whitespace per line.
*/
public static long parseRangeBody(String body, String suffix) {
if (body == null || suffix == null || suffix.isEmpty()) {
return 0;
}
for (String line : body.split("\\r?\\n")) {
int idx = line.indexOf(':');
if (idx == -1) {
continue;
}
if (line.substring(0, idx).trim().equals(suffix)) {
try {
long count = Long.parseLong(line.substring(idx + 1).trim());
return count < 0 ? 0 : count;
} catch (NumberFormatException e) {
return 0;
}
}
}
return 0;
}
/** Count the "SUFFIX:COUNT" candidate lines in a range response. */
public static int countCandidates(String body) {
if (body == null) {
return 0;
}
int n = 0;
for (String line : body.split("\\r?\\n")) {
int idx = line.indexOf(':');
if (idx != -1 && !line.substring(0, idx).trim().isEmpty()) {
n++;
}
}
return n;
}
/**
* Assemble the local part of a breach check: hash the password, split it,
* and evaluate a fetched range body. The fetch itself is transport and lives
* in the caller (java.net.http.HttpClient GET HIBP_RANGE_URL + prefix).
*/
public static BreachResult evaluate(String password, String rangeBody) throws Exception {
HashParts parts = splitHash(sha1Hex(password));
long count = parseRangeBody(rangeBody, parts.suffix());
return new BreachResult(count > 0, count, parts.prefix(), parts.suffix(), null);
}
}
Also available in 9 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 →