Java random string generator
Generate one here, or copy the Java below. Every example uses SecureRandom and was run on Java 21.
More options
The short answer
Create one SecureRandom, keep it, and for each character call nextInt(alphabet.length()) and append the character at that index. nextInt with a bound discards out-of-range values internally, so every character is equally likely. SecureRandom is safe to share between threads, so one static instance is enough.
For a token rather than a string over a particular alphabet, fill a byte array with nextBytes and encode it with Base64.getUrlEncoder().withoutPadding(). Thirty-two bytes become 43 URL-safe characters carrying 256 bits.
new SecureRandom() picks the platform default algorithm. On Windows with Java 21 that was DRBG when checked for this page. SecureRandom.getInstanceStrong() picks whatever the securerandom.strongAlgorithms security property names instead; on the same Windows JDK that was Windows-PRNG. Ordinary tokens and passwords do not need the strong variant.
| Class | What it is | Safe for secrets |
|---|---|---|
| java.util.Random | 48-bit linear congruential generator | No |
| Math.random() | A shared java.util.Random | No |
| ThreadLocalRandom | Fast, per-thread, non-cryptographic | No |
| SplittableRandom | Fast, splittable, non-cryptographic | No |
| SecureRandom | Cryptographically secure generator | Yes |
| UUID.randomUUID() | Drawn from a SecureRandom | Yes |
Why java.util.Random fails
java.util.Random is a 48-bit linear congruential generator. Each nextInt() returns the top 32 bits of the new state, which leaves only 16 unknown bits, so two consecutive values are enough to recover the state by trying every possibility and predict everything that follows. Math.random() is backed by the same kind of generator.
Seeding it from System.currentTimeMillis() or System.nanoTime() does not help, because the seed is guessable and the generator is predictable regardless. new Random(42) returns the same sequence on every run, which is useful in a unit test and fatal in a token.
RandomStringUtils depends on the release you have
Apache Commons RandomStringUtils is the most common library answer, and whether randomAlphanumeric is safe depends entirely on which commons-lang3 version is on your classpath. Its source of randomness has changed three times. The table was built by disassembling each release from Maven Central.
| commons-lang3 | Source | Safe for secrets |
|---|---|---|
| 3.12.0 | java.util.Random | No |
| 3.13.0 and 3.14.0 | ThreadLocalRandom | No |
| 3.15.0 | SecureRandom.getInstanceStrong() | Yes |
| 3.16.0 | secure(), backed by SecureRandom | Yes |
| 3.17.0 to 3.20.0 | secure(), with the static methods deprecated | Yes |
On 3.16.0 or later, write RandomStringUtils.secure().nextAlphanumeric(24) so the choice is visible in the code. On anything older, write the few lines of SecureRandom code yourself rather than rely on a static method whose source of randomness changed between releases.
Mistakes that pass code review
Each of these compiles, runs and produces plausible output.
- alphabet.charAt(Math.abs(random.nextInt()) % n). Math.abs(Integer.MIN_VALUE) is still negative, so this throws once in about four billion calls, and the remainder is biased as well. Use nextInt(n).
- UUID.randomUUID().toString().substring(0, 8) as a short ID. The source is sound, but eight hex characters are 32 bits, and a collision becomes more likely than not after about 77,000 values.
- Long.toString(random.nextLong(), 36) as a token. The length varies, half of all values start with a minus sign, and it never carries more than 64 bits.
- Base64.getEncoder() for a token in a URL. The standard encoder emits plus, slash and equals. Use Base64.getUrlEncoder().withoutPadding().
- A new SecureRandom() created inside the loop for every character. It is correct but wasteful. Create one instance and reuse it.
Many unique strings
Add strings to a HashSet until it reaches the size you need. At 16 characters from 62 a collision is so unlikely that the loop almost never repeats, and the set makes uniqueness certain. If the values are stored, back that up with a unique constraint in the database, since a set in one JVM knows nothing about another.
Doing this in your own code
Java 8+ - SecureRandom
import java.security.SecureRandom;
public class Main {
private static final String ALPHABET =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
// SecureRandom is thread-safe; create it once and share it.
private static final SecureRandom RANDOM = new SecureRandom();
static String randomString(int length) {
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
// nextInt(bound) discards out-of-range values, so there is no bias.
sb.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));
}
return sb.toString();
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
System.out.println(randomString(24));
}
}
} Java 8+ - URL-safe and hex tokens
import java.security.SecureRandom;
import java.util.Base64;
public class Main {
public static void main(String[] args) {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[32];
random.nextBytes(bytes);
// 43 characters, 256 bits, safe in URLs and headers
System.out.println(Base64.getUrlEncoder().withoutPadding().encodeToString(bytes));
// 64 hex characters, 256 bits
StringBuilder hex = new StringBuilder(64);
for (byte b : bytes) hex.append(String.format("%02x", b));
System.out.println(hex);
}
} On Java 17 and later, HexFormat.of().formatHex(bytes) replaces the formatting loop.
Java with commons-lang3 3.16.0+
import org.apache.commons.lang3.RandomStringUtils;
public class Main {
public static void main(String[] args) {
// secure() is backed by SecureRandom. It exists from 3.16.0 onward.
System.out.println(RandomStringUtils.secure().nextAlphanumeric(24));
System.out.println(RandomStringUtils.secure().nextAlphanumeric(24));
System.out.println(RandomStringUtils.secure().nextAlphanumeric(24));
}
} Executed against commons-lang3 3.20.0. Avoid RandomStringUtils.insecure() for anything secret.
Java 8+ - password with every character type
import java.security.SecureRandom;
public class Main {
private static final String[] CLASSES = {
"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", "0123456789", "!#$%*+-=?@^_"
};
private static final String ALPHABET = String.join("", CLASSES);
private static final SecureRandom RANDOM = new SecureRandom();
static String password(int length) {
if (length < CLASSES.length) {
throw new IllegalArgumentException("length is shorter than the number of required classes");
}
while (true) {
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
sb.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));
}
String candidate = sb.toString();
boolean valid = true;
for (String cls : CLASSES) {
if (candidate.chars().noneMatch(c -> cls.indexOf(c) >= 0)) valid = false;
}
// Whole-string rejection keeps the result uniform over valid passwords.
if (valid) return candidate;
}
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) System.out.println(password(20));
}
} Questions
How do I generate a random string in Java?
Create a static final SecureRandom, then build the string with a StringBuilder, appending ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())) once per character. This is secure and unbiased on every Java version from 8 onward.
Is java.util.Random secure?
No. It is a 48-bit linear congruential generator, and two consecutive nextInt() values are enough to reconstruct its state and predict all later output. Math.random(), ThreadLocalRandom and SplittableRandom are not cryptographic either. Use SecureRandom for anything that must be unguessable.
Is RandomStringUtils.randomAlphanumeric secure?
It depends on the version. In commons-lang3 3.12.0 it used java.util.Random, and in 3.13.0 and 3.14.0 ThreadLocalRandom, neither of which is secure. From 3.15.0 it draws from SecureRandom, and from 3.16.0 the explicit form is RandomStringUtils.secure().nextAlphanumeric(n).
Should I use SecureRandom.getInstanceStrong()?
Usually not. new SecureRandom() is already a cryptographically secure generator suitable for tokens, passwords and keys. getInstanceStrong() selects the algorithm named by the securerandom.strongAlgorithms security property, which is intended for long-lived high-value keys.
How do I generate a URL-safe random token in Java?
Fill a 32-byte array with SecureRandom.nextBytes, then call Base64.getUrlEncoder().withoutPadding().encodeToString(bytes). The result is 43 characters of letters, digits, hyphen and underscore, carrying 256 bits.