Erzeugen Sie ein sicheres zufälliges Passwort in Java

Generieren Sie ein sicheres zufälliges Passwort in Java

1. Einführung

In diesem Tutorial werden verschiedene Methoden vorgestellt, mit denen wir ein sicheres zufälliges Passwort in Java generieren können.

In unseren Beispielen werden Kennwörter mit zehn Zeichen generiert, die jeweils mindestens zwei Kleinbuchstaben, zwei Großbuchstaben, zwei Ziffern und zwei Sonderzeichen enthalten.

2. Passay verwenden

Passay ist eine Bibliothek zur Durchsetzung von Kennwortrichtlinien. Insbesondere können wir die Bibliothek verwenden, um das Kennwort mithilfe eines konfigurierbaren Regelsatzes zu generieren.

Mit Hilfe der Standardimplementierungen vonCharacterDatakönnen wir die für das Kennwort erforderlichen Regeln formulieren. Außerdem können wirformulate custom CharacterData implementations to suit our requirements: **

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

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

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

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

        public String getCharacters() {
            return "[email protected]#$%^&*()_+";
        }
    };
    CharacterRule splCharRule = new CharacterRule(specialChars);
    splCharRule.setNumberOfCharacters(2);

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

Hier haben wir eine benutzerdefinierteCharacterData-Implementierung für Sonderzeichen erstellt. Auf diese Weise können wir die Anzahl der zulässigen Zeichen einschränken.

Abgesehen davon verwenden wir für unsere anderen Regeln Standardimplementierungen vonCharacterData.

Lassen Sie uns nun unseren Generator anhand eines Komponententests überprüfen. Zum Beispiel können wir das Vorhandensein von zwei Sonderzeichen überprüfen:

@Test
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) {
            specialCharCount++;
        }
    }
    assertTrue("Password validation failed in Passay", specialCharCount >= 2);
}

Es ist erwähnenswert, dassalthough Passay is open source, it is dual licensed under both LGPL and Apache 2. Wie bei jeder Software von Drittanbietern müssen wir sicherstellen, dass diese Lizenzen eingehalten werden, wenn wir sie in unseren Produkten verwenden. Auf der GNU-Website finden Sie weitere Informationen zuthe LGPL and Java.

3. Verwenden vonRandomStringGenerator

Schauen wir uns als nächstes dieRandomStringGenerator inApache Commons Text an. MitRandomStringGenerator, können wir Unicode-Zeichenfolgen generieren, die die angegebene Anzahl von Codepunkten enthalten.

Jetzt erstellen wir eine Instanz des Generators mithilfe der KlasseRandomStringGenerator.Builder. Natürlich können wir auch die Eigenschaften des Generators weiter manipulieren.

Mit Hilfe des Builders können wir die Standardimplementierung der Zufälligkeit leicht ändern. Darüber hinaus können wir auch die Zeichen definieren, die in der Zeichenfolge zulässig sind:

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

Eine Einschränkung bei der Verwendung vonRandomStringGenerator besteht darin, dass eslacks the ability to specify the number of characters in each set, like in Passay. ist. Wir können dies jedoch umgehen, indem wir die Ergebnisse mehrerer Mengen zusammenführen:

public String generateCommonTextPassword() {
    String pwString = generateRandomSpecialCharacters(2).concat(generateRandomNumbers(2))
      .concat(generateRandomAlphabet(2, true))
      .concat(generateRandomAlphabet(2, false))
      .concat(generateRandomCharacters(2));
    List pwChars = pwString.chars()
      .mapToObj(data -> (char) data)
      .collect(Collectors.toList());
    Collections.shuffle(pwChars);
    String password = pwChars.stream()
      .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
      .toString();
    return password;
}

Überprüfen Sie als Nächstes das generierte Kennwort, indem Sie die Kleinbuchstaben überprüfen:

@Test
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) {
            lowerCaseCount++;
        }
    }
    assertTrue("Password validation failed in commons-text ", lowerCaseCount >= 2);
}

Standardmäßig verwendetRandomStringGeneratorThreadLocalRandom für die Zufälligkeit. Nun ist es wichtig,that this does not ensure cryptographic security zu erwähnen.

Wir können die Zufallsquelle jedoch mitusingRandom(TextRandomProvider). festlegen. Beispielsweise können wirSecureTextRandomProvider für die kryptografische Sicherheit verwenden:

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

4. Verwenden vonRandomStringUtils

Eine andere Option, die wir verwenden könnten, ist die KlasseRandomStringUtilsinApache Commons Lang Library. Diese Klasse macht mehrere statische Methoden verfügbar, die wir für unsere Problemstellung verwenden können.

Lassen Sie uns sehen, wie wir den Bereich von Codepunkten bereitstellen können, die für das Kennwort akzeptabel sind:

 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)
      .concat(numbers)
      .concat(specialChar)
      .concat(totalChars);
    List pwdChars = combinedChars.chars()
      .mapToObj(c -> (char) c)
      .collect(Collectors.toList());
    Collections.shuffle(pwdChars);
    String password = pwdChars.stream()
      .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
      .toString();
    return password;
}

Um das generierte Passwort zu überprüfen, überprüfen wir die Anzahl der numerischen Zeichen:

@Test
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) {
            numCount++;
        }
    }
    assertTrue("Password validation failed in commons-lang3", numCount >= 2);
}

Hier verwendetRandomStringUtils standardmäßigRandom als Zufallsquelle. Es gibt jedoch eine Methode in der Bibliothek, mit der wir die Quelle der Zufälligkeit angeben können:

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

Jetzt könnten wir die kryptografische Sicherheit mit einer Instanz vonSecureRandom gewährleisten. Diese Funktionalität kann jedoch nicht auf andere Methoden in der Bibliothek erweitert werden. Nebenbei bemerkt,Apache advocates the usage of RandomStringUtils for simple use cases only.

5. Verwenden einer benutzerdefinierten Dienstprogrammmethode

Wir können auch dieSecureRandom-Klasse verwenden, um eine benutzerdefinierte Dienstprogrammklasse für unser Szenario zu erstellen. Lassen Sie uns zunächst eine Zeichenfolge mit Sonderzeichen der Länge zwei generieren:

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

Beachten Sie außerdem, dass33 und45 den Bereich der Unicode-Zeichen bezeichnen. Jetzt können wir mehrere Streams gemäß unseren Anforderungen generieren. Dann können wir die Ergebnismengen zusammenführen, um das erforderliche Passwort zu generieren:

public String generateSecureRandomPassword() {
    Stream pwdStream = Stream.concat(getRandomNumbers(2),
      Stream.concat(getRandomSpecialChars(2),
      Stream.concat(getRandomAlphabets(2, true), getRandomAlphabets(4, false))));
    List charList = pwdStream.collect(Collectors.toList());
    Collections.shuffle(charList);
    String password = charList.stream()
        .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
        .toString();
    return password;
}

Überprüfen wir nun das generierte Passwort für die Anzahl der Sonderzeichen:

@Test
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) {
            specialCharCount++;
        }
    }
    assertTrue("Password validation failed in Secure Random", specialCharCount >= 2);
}

6. Fazit

In diesem Tutorial konnten wir Passwörter gemäß unseren Anforderungen mithilfe verschiedener Bibliotheken generieren.

Wie immer sind die im Artikel verwendeten Codebeispieleover on GitHub verfügbar.