Générer un mot de passe aléatoire sécurisé en Java

Générer un mot de passe aléatoire sécurisé en Java

1. introduction

Dans ce didacticiel, nous examinerons différentes méthodes que nous pouvons utiliser pour générer un mot de passe aléatoire sécurisé en Java.

Dans nos exemples, nous allons générer des mots de passe à dix caractères, chacun comportant au minimum deux caractères minuscules, deux caractères majuscules, deux chiffres et deux caractères spéciaux.

2. Utiliser Passay

Passay est une bibliothèque d'application de politique de mot de passe. Nous pouvons notamment utiliser la bibliothèque pour générer le mot de passe à l'aide d'un ensemble de règles configurable.

Avec l'aide des implémentations par défaut deCharacterData, nous pouvons formuler les règles requises pour le mot de passe. De plus, nous pouvonsformulate 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;
}

Ici, nous avons créé une implémentationCharacterData personnalisée pour les caractères spéciaux. Cela nous permet de restreindre le jeu de caractères valides autorisés.

En dehors de cela, nous utilisons les implémentations par défaut deCharacterData pour nos autres règles.

Maintenant, vérifions notre générateur par rapport à un test unitaire. Par exemple, nous pouvons vérifier la présence de deux caractères spéciaux:

@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);
}

Il convient de noter quealthough Passay is open source, it is dual licensed under both LGPL and Apache 2. Comme pour tout logiciel tiers, nous devons nous assurer de respecter ces licences lorsque nous les utilisons dans nos produits. Le site Web GNU contient plus d'informations surthe LGPL and Java.

3. Utilisation deRandomStringGenerator

Voyons ensuite lesRandomStringGenerator dansApache Commons Text. AvecRandomStringGenerator,, nous pouvons générer des chaînes Unicode contenant le nombre spécifié de points de code.

Maintenant, nous allons créer une instance du générateur en utilisant la classeRandomStringGenerator.Builder. Bien sûr, nous pouvons également manipuler davantage les propriétés du générateur.

Avec l'aide du constructeur, nous pouvons facilement modifier l'implémentation par défaut de l'aléatoire. De plus, nous pouvons également définir les caractères autorisés dans la chaîne:

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

Maintenant, une limitation de l'utilisation deRandomStringGenerator est qu'illacks the ability to specify the number of characters in each set, like in Passay. Cependant, nous pouvons contourner cela en fusionnant les résultats de plusieurs ensembles:

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;
}

Ensuite, validons le mot de passe généré en vérifiant les lettres minuscules:

@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);
}

Par défaut,RandomStringGenerator utiliseThreadLocalRandom pour le caractère aléatoire. Maintenant, il est important de mentionnerthat this does not ensure cryptographic security.

Cependant, nous pouvons définir la source du caractère aléatoire en utilisantusingRandom(TextRandomProvider). Par exemple, nous pouvons utiliserSecureTextRandomProvider pour la sécurité cryptographique:

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. Utilisation deRandomStringUtils

Une autre option que nous pourrions utiliser est la classeRandomStringUtils dans lesApache Commons Lang Library. Cette classe expose plusieurs méthodes statiques que nous pouvons utiliser pour notre énoncé de problème.

Voyons comment nous pouvons fournir la plage de points de code acceptables pour le mot de passe:

 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;
}

Pour valider le mot de passe généré, vérifions le nombre de caractères numériques:

@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);
}

Ici,RandomStringUtils utiliseRandom par défaut comme source d'aléa. Cependant, il existe une méthode dans la bibliothèque qui nous permet de spécifier la source du caractère aléatoire:

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

Maintenant, nous pourrions assurer la sécurité cryptographique en utilisant une instance deSecureRandom. Toutefois, cette fonctionnalité ne peut pas être étendue à d'autres méthodes de la bibliothèque. En passant,Apache advocates the usage of RandomStringUtils for simple use cases only.

5. Utilisation d'une méthode d'utilitaire personnalisé

Nous pouvons également utiliser la classeSecureRandom pour créer une classe utilitaire personnalisée pour notre scénario. Pour commencer, générons une chaîne de caractères spéciaux de longueur deux:

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

Notez également que33 et45 indiquent la plage de caractères Unicode. Maintenant, nous pouvons générer plusieurs flux selon nos besoins. Ensuite, nous pouvons fusionner les jeux de résultats pour générer le mot de passe requis:

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;
}

Maintenant, validons le mot de passe généré pour le nombre de caractères spéciaux:

@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. Conclusion

Dans ce tutoriel, nous avons pu générer des mots de passe, conformes à nos exigences, en utilisant différentes bibliothèques.

Comme toujours, les exemples de code utilisés dans l'article sont disponiblesover on GitHub.