简体   繁体   中英

How to guarantee certain characters in a random password

I am using this function to generate a random password of length 11, as seen in this post:

import java.util.Random;

public class RandomPassword {
    public static void main(String[] args){
        RandomPassword r = new RandomPassword(11);
        System.out.print(r.nextString());

    }
      private static final char[] symbols;

      static {
        StringBuilder tmp = new StringBuilder();

        for (char ch = '0'; ch <= '9'; ++ch)
          tmp.append(ch);
        for (char ch = 'a'; ch <= 'z'; ++ch)
          tmp.append(ch);
        for (char ch = 'A'; ch <= 'Z'; ++ch)
              tmp.append(ch);


        symbols = tmp.toString().toCharArray();
      }   

      private final Random random = new Random();

      private final char[] buf;

      public RandomPassword(int length) {
        if (length < 1)
          throw new IllegalArgumentException("length < 1: " + length);
        buf = new char[length];
      }

      public String nextString() {
        for (int idx = 0; idx < buf.length; ++idx) 
          buf[idx] = symbols[random.nextInt(symbols.length)];
        return new String(buf);
      }
}

However I need to modify it so that I can guarantee at least one capital letter, one number and one lowercase letter. Right now there is the possibility that the password could contain all caps/lowercase/numbers and we need at least one of each to be accepted by AD. Any help is appreciated, thanks.

***I am told that it would be best to loop through in nextString() like this:

public String nextString() {
        for (int idx = 0; idx < buf.length; ++idx) 
          if(buf[buf.length%3].equals(num/lower/cap)){
            buf[idx] = symbols[random.nextInt(symbols.length)];
        }
        .
        .
        .
        return new String(buf);
      }

What do you think?

  1. Create a truly random password
  2. See if the random password meets your requirements using a regular expression
  3. If not, pick a random char from the password and modify it to be a randomly chosen symbol (or number, or uppercase letter)

This would prevent loops and would also have the benefit of being truly random.

Or, if you're ok with losing some entropy you could:

  1. Create four random strings: lowercase (length: 5), uppercase (4), number (1) and symbol (1)
  2. Concatenate the strings
  3. Shuffle the string with Collections.shuffle

Loop over the characters in the array to ensure that each character class is contained within. Then, if it fails, just generate a new random string. This ensures that all are contained and will not fail to validate very often.

I would recommend a regular expresion searching for a set of predifined characters/numbers uppercase/lowercase and a error message

   public String getPassword(){
      SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "SUN");
      byte[] salt = new byte[16];
      sr.nextBytes(salt);

      return wellformedPassword(salt.toString()) ? salt.toString() : secured(salt.toString());

   }

   public String secured(String pass){
       return string1.replaceFirst(Pattern.quote("[a-z]", "Z");
   }

   public boolean wellformedPassword(String pass){
      return checkRegExp(pass, "\\d") 
              && checkRegExp(pass, "[A-Z]")
              && checkRegExp(pass, "[a-z]");
   }

   public boolean checkRegExp(String str, String pattern){
      Pattern p = Pattern.compile(pattern);
      Matcher n = p.matcher(str);
      return n.find();
   } 

SHA assure you lowercase letters and numbers, then you just need to turn, one (in my case) or more to upper.

this is a dummy approach wich can be improved. Share your code so i cant edit later my answer for anyone else.

You could get that char and turn it to upper instead of Z but i guess with this you can get started.

This "cheats" (is weaker than it could be), but it meets the usual requirements (or can be modified to meet them):

import java.util.Random;
public class Password {
    private static final String UPPER = "ABCDEFGHIJKLMNPQRSTUVWXYZ";
    private static final String LOWER = "abcdefghijklmnpqrstuvwxyz";
    private static final String NUMBER = "123456789";
    private static final String SPECIAL = "!@#$%&*+?";
    private Random randGen = new Random();
    public static void main(String[] argv) {
        Password me = new Password();
        for (int i = 0; i <= 10; i++) {
            me.printPassword();
        }
    }
    private void printPassword() {
        StringBuffer buf = new StringBuffer();
        buf.append(LOWER.charAt(Math.abs(randGen.nextInt()) % LOWER.length()));
        buf.append(LOWER.charAt(Math.abs(randGen.nextInt()) % LOWER.length()));
        buf.append(NUMBER.charAt(Math.abs(randGen.nextInt()) % NUMBER.length()));
        for (int i = 0; i <= 4; i++) {
            buf.append(LOWER.charAt(Math.abs(randGen.nextInt()) % LOWER.length()));
        }
        buf.append(UPPER.charAt(Math.abs(randGen.nextInt()) % UPPER.length()));
        buf.append(LOWER.charAt(Math.abs(randGen.nextInt()) % LOWER.length()));
        System.out.println(buf.toString());
    }
}

I had another algorithm that was better, but lost it.

(I like to print out multiple "suggestions" and then pick the one that seems easiest to remember.)

I really don't know how reliable it is but a try

List<Character> listOfUpperCaseLetter = new ArrayList<Character>();
for(int i = 65;i<91;i++){
    char ch = (char)i;
    listOfUpperCaseLetter.add(ch);
}
List<Character> listOfLowerCaseLetter = new ArrayList<Character>();
for(int i = 97;i<123;i++){
    char ch = (char)i;
    listOfLowerCaseLetter.add(ch);
}
List<Integer> listOfInt = new ArrayList<Integer>();
for(int i =0;i<10;i++){
    listOfInt.add(i);
}
StringBuilder br = new StringBuilder();
Collections.shuffle(listOfLowerCaseLetter);
Collections.shuffle(listOfUpperCaseLetter);
Collections.shuffle(listOfInt);
br.append(listOfUpperCaseLetter.get(0));
br.append(listOfLowerCaseLetter.get(1));
br.append(listOfLowerCaseLetter.get(3));
br.append(listOfInt.get(1));
br.append(listOfInt.get(0));
br.append(listOfLowerCaseLetter.get(2));
br.append(listOfUpperCaseLetter.get(1));
br.append(listOfLowerCaseLetter.get(0));
br.append(listOfUpperCaseLetter.get(2));
br.append(listOfInt.get(2));

System.out.println(br.toString());

I simply added an if/else statement saying to add certain members of the symbols array (0-10, 11-35, 36-61). This guarantees what is needed. Sometimes the simplest answer is the best answer. Thanks for the ideas.

          if (idx == 0){
            buf[idx] = symbols[random.nextInt(10)]; 
          }else if (idx == 1){
            buf[idx] = symbols[10 + random.nextInt(35-10+1)];
          }else if(idx == 2){
            buf[idx] = symbols[36 + random.nextInt(61-36+1)];
          }else{
            buf[idx] = symbols[random.nextInt(symbols.length)];
          }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM