Secure Token Generator — Java source
Generate cryptographically-secure random tokens in your browser. Pick the entropy size and format - hex, base32, base64, base62, or alphanumeric - and see the real strength in bits. Runs entirely client-side.
This is the Java implementation — the same logic the interactive tool runs, in a shareable, citable form.
// token-generator — cryptographically-secure random tokens in hex/base32/base64/base62/... alphabets, with unbiased symbol selection and entropy/strength estimates. Java (17+) port of src/lib/token-generator.ts — same logic as this dir's javascript.js; the full 8-entry alphabet table, custom alphabets and injectable RNG omitted for the 80-line budget (see javascript.js / python.py).
import java.security.SecureRandom;
import java.util.LinkedHashMap;
import java.util.Map;
public class TokenGenerator {
// The fixed alphabets (subset).
static final Map<String, String> ALPHABETS = new LinkedHashMap<>(Map.of(
"hex", "0123456789abcdef",
"base32", "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", // RFC 4648
"base64url", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
"base62", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
static final SecureRandom RNG = new SecureRandom(); // the CSPRNG
private static double log2(double x) {
return Math.log(x) / Math.log(2);
}
// Resolve an alphabet name to its symbol string; unknown -> hex (the default).
static String resolveAlphabet(String name) {
return ALPHABETS.getOrDefault(name, ALPHABETS.get("hex"));
}
// Characters needed to carry `bytes` bytes of entropy through `size` symbols.
static int outputLength(double bytes, int size) {
if (bytes < 1 || size < 2) return 0;
return (int) Math.ceil(bytes * 8 / log2(size));
}
// Pick alphabet[n] WITHOUT modulo bias: naive draw % size favors trailing
// symbols whenever size does not divide the draw range (base62 etc.), so
// any draw at or above the largest multiple of size fitting in 32 bits is
// rejected and redrawn. The 64-redraw cap stops a broken RNG from looping
// forever — same guard as the TS/Python reference. nextInt() is treated
// as unsigned so the math matches the 32-bit reference exactly.
static String selectUnbiased(String alphabet, int n) {
int size = alphabet.length();
int limit = Integer.divideUnsigned(-1, size) * size; // 0xFFFFFFFF / size * size
StringBuilder out = new StringBuilder(n);
for (int i = 0; i < n; i++) {
int x = RNG.nextInt(); // one secure 32-bit draw
for (int guard = 0; Integer.compareUnsigned(x, limit) >= 0 && guard < 64; guard++)
x = RNG.nextInt();
out.append(alphabet.charAt(Integer.remainderUnsigned(x, size)));
}
return out.toString();
}
// Bucket an entropy estimate (bits) into the tool's strength tiers:
// weak <64 · fair 64-127 · strong 128-255 · very strong >=256.
static String strengthLabel(double bits) {
if (bits < 64) return "weak";
if (bits < 128) return "fair";
if (bits < 256) return "strong";
return "very strong";
}
// Demo: the island's three showcase rows — 16-byte hex, 20-byte base62,
// 32-byte base32 — each labeled with its real strength in bits.
public static void main(String[] args) {
String[][] demos = {{"hex", "16"}, {"base62", "20"}, {"base32", "32"}};
for (String[] d : demos) {
double bytes = Double.parseDouble(d[1]);
String alphabet = resolveAlphabet(d[0]);
int n = outputLength(bytes, alphabet.length());
String token = selectUnbiased(alphabet, n);
double bits = n * log2(alphabet.length());
System.out.printf("%-8s %.0f bytes -> %s (%.0f bits, %s)%n",
d[0], bytes, token, bits, strengthLabel(bits));
}
}
}
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 →