Generate a Secure Random Password in Java – 在Java中生成一个安全的随机密码

最后修改: 2018年 11月 7日

1. Introduction


In this tutorial, we’ll look at various methods we can use to generate a secure random password in Java.


In our examples, we’ll be generating ten-character passwords, each with a minimum of two lower case characters, two uppercase characters, two digits, and two special characters.


2. Using Passay


Passay is a password policy enforcement library. Notably, we can make use of the library to generate the password using a configurable ruleset.


With the help of the default CharacterData implementations, we can formulate the rules required for the password. Furthermore, we can formulate custom CharacterData implementations to suit our requirements:


public String generatePassayPassword() {
    PasswordGenerator gen = new PasswordGenerator();
    CharacterData lowerCaseChars = EnglishCharacterData.LowerCase;
    CharacterRule lowerCaseRule = new CharacterRule(lowerCaseChars);

    CharacterData upperCaseChars = EnglishCharacterData.UpperCase;
    CharacterRule upperCaseRule = new CharacterRule(upperCaseChars);

    CharacterData digitChars = EnglishCharacterData.Digit;
    CharacterRule digitRule = new CharacterRule(digitChars);

    CharacterData specialChars = new CharacterData() {
        public String getErrorCode() {
            return ERROR_CODE;

        public String getCharacters() {
            return "!@#$%^&*()_+";
    CharacterRule splCharRule = new CharacterRule(specialChars);

    String password = gen.generatePassword(10, splCharRule, lowerCaseRule, 
      upperCaseRule, digitRule);
    return password;

Here, we’ve created a custom CharacterData implementation for special characters. This allows us to restrict the set of valid characters allowed.


Apart from that, we’re making use of default implementations of CharacterData for our other rules.


Now, let’s check our generator against a unit test. For instance, we can check the presence of two special characters:


public void whenPasswordGeneratedUsingPassay_thenSuccessful() {
    RandomPasswordGenerator passGen = new RandomPasswordGenerator();
    String password = passGen.generatePassayPassword();
    int specialCharCount = 0;
    for (char c : password.toCharArray()) {
        if (c >= 33 || c <= 47) {
    assertTrue("Password validation failed in Passay", specialCharCount >= 2);

It’s worth noting that although Passay is open source, it is dual licensed under both LGPL and Apache 2. As with any third-party software, we must be sure to comply with these licenses when we use it in our products. The GNU website has more information about the LGPL and Java.

值得注意的是,虽然Passay是开源的,但它是在LGPL和Apache 2下的双重许可。与任何第三方软件一样,当我们在产品中使用它时,我们必须确保遵守这些许可证的规定。GNU网站有关于LGPL和Java的更多信息。

3. Using RandomStringGenerator


Next, let’s look at the RandomStringGenerator in Apache Commons Text. With RandomStringGenerator, we can generate Unicode strings containing the specified number of code points.

接下来,让我们看看Apache Commons Text中的RandomStringGenerator。通过RandomStringGenerator,我们可以生成包含指定码位数的Unicode字符串。

Now, we’ll create an instance of the generator by using the RandomStringGenerator.Builder class. Of course, we can also further manipulate the properties of the generator.


With the help of the builder, we can easily change the default implementation of randomness. Moreover, we can also define the characters that are allowed in the string:


public String generateRandomSpecialCharacters(int length) {
    RandomStringGenerator pwdGenerator = new RandomStringGenerator.Builder().withinRange(33, 45)
    return pwdGenerator.generate(length);

Now, one limitation of using RandomStringGenerator is that it lacks the ability to specify the number of characters in each set, like in Passay. However, we can circumvent that by merging the results of multiple sets:


public String generateCommonTextPassword() {
    String pwString = generateRandomSpecialCharacters(2).concat(generateRandomNumbers(2))
      .concat(generateRandomAlphabet(2, true))
      .concat(generateRandomAlphabet(2, false))
    List<Character> pwChars = pwString.chars()
      .mapToObj(data -> (char) data)
    String password =
      .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
    return password;

Next, let’s validate the generated password by verifying the lowercase letters:


public void whenPasswordGeneratedUsingCommonsText_thenSuccessful() {
    RandomPasswordGenerator passGen = new RandomPasswordGenerator();
    String password = passGen.generateCommonTextPassword();
    int lowerCaseCount = 0;
    for (char c : password.toCharArray()) {
        if (c >= 97 || c <= 122) {
    assertTrue("Password validation failed in commons-text ", lowerCaseCount >= 2);

By default, RandomStringGenerator makes use of ThreadLocalRandom for randomness. Now, it’s important to mention that this does not ensure cryptographic security.


However, we can set the source of randomness using usingRandom(TextRandomProvider). For instance, we can make use of SecureTextRandomProvider for cryptographic security:


public String generateRandomSpecialCharacters(int length) {
    SecureTextRandomProvider stp = new SecureTextRandomProvider();
    RandomStringGenerator pwdGenerator = new RandomStringGenerator.Builder()
      .withinRange(33, 45)
    return pwdGenerator.generate(length);

4. Using RandomStringUtils


Another option that we could employ is the RandomStringUtils class in the Apache Commons Lang Library. This class exposes several static methods that we can use for our problem statement.

我们可以采用的另一个选择是Apache Commons Lang Library中的RandomStringUtils类。这个类暴露了几个静态方法,我们可以将其用于我们的问题陈述。

Let’s see how we can provide the range of code points that are acceptable for the password:


 public String generateCommonLangPassword() {
    String upperCaseLetters = RandomStringUtils.random(2, 65, 90, true, true);
    String lowerCaseLetters = RandomStringUtils.random(2, 97, 122, true, true);
    String numbers = RandomStringUtils.randomNumeric(2);
    String specialChar = RandomStringUtils.random(2, 33, 47, false, false);
    String totalChars = RandomStringUtils.randomAlphanumeric(2);
    String combinedChars = upperCaseLetters.concat(lowerCaseLetters)
    List<Character> pwdChars = combinedChars.chars()
      .mapToObj(c -> (char) c)
    String password =
      .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
    return password;

To validate the generated password, let’s verify the number of numeric characters:


public void whenPasswordGeneratedUsingCommonsLang3_thenSuccessful() {
    RandomPasswordGenerator passGen = new RandomPasswordGenerator();
    String password = passGen.generateCommonsLang3Password();
    int numCount = 0;
    for (char c : password.toCharArray()) {
        if (c >= 48 || c <= 57) {
    assertTrue("Password validation failed in commons-lang3", numCount >= 2);

Here, RandomStringUtils makes use of Random by default as the source of randomness. However, there is a method within the library that lets us specify the source of randomness:


String lowerCaseLetters = RandomStringUtils.
  random(2, 97, 122, true, true, null, new SecureRandom());

Now, we could ensure cryptographic security using an instance of SecureRandom. However, this functionality cannot be extended to other methods in the library. On a side note, Apache advocates the usage of RandomStringUtils for simple use cases only.


5. Using a Custom Utility Method


We can also make use of the SecureRandom class to create a custom utility class for our scenario. For starters, let’s generate a string of special characters of length two:


public Stream<Character> getRandomSpecialChars(int count) {
    Random random = new SecureRandom();
    IntStream specialChars = random.ints(count, 33, 45);
    return specialChars.mapToObj(data -> (char) data);

Also, notice that 33 and 45 denote the range of Unicode characters. Now, we can generate multiple streams as per our requirements. Then we can merge the result sets to generate the required password:


public String generateSecureRandomPassword() {
    Stream<Character> pwdStream = Stream.concat(getRandomNumbers(2), 
      Stream.concat(getRandomAlphabets(2, true), getRandomAlphabets(4, false))));
    List<Character> charList = pwdStream.collect(Collectors.toList());
    String password =
        .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
    return password;

Now, let’s validate the generated password for the number of special characters:


public void whenPasswordGeneratedUsingSecureRandom_thenSuccessful() {
    RandomPasswordGenerator passGen = new RandomPasswordGenerator();
    String password = passGen.generateSecureRandomPassword();
    int specialCharCount = 0;
    for (char c : password.toCharArray()) {
        if (c >= 33 || c <= 47) {
    assertTrue("Password validation failed in Secure Random", specialCharCount >= 2);

6. Conclusion


In this tutorial, we were able to generate passwords, conforming to our requirements, using different libraries.


As always, the code samples used in the article are available over on GitHub.