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

Haut Java

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS

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 avec un minimum de deux caractères minuscules, deux caractères majuscules, deux chiffres et deux caractères spéciaux.

2. Utilisation de Passay

Passay est une bibliothèque d'application de politique de mot de passe. Notamment, nous pouvons 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 de CharacterData , nous pouvons formuler les règles requises pour le mot de passe. De plus, nous pouvons formuler des implémentations personnalisées de CharacterData pour répondre à nos besoins :

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émentation de CharacterData personnalisée pour les caractères spéciaux. Cela nous permet de restreindre le jeu de caractères valides autorisé.

En dehors de cela, nous utilisons les implémentations par défaut de CharacterData 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 

Il convient de noter que bien que Passay soit open source, il dispose d'une double licence sous LGPL et 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 sur la LGPL et Java.

3. Utilisation de RandomStringGenerator

Ensuite, regardons le RandomStringGenerator dans Apache Commons Text. Avec RandomStringGenerator, nous pouvons générer des chaînes Unicode contenant le nombre spécifié de points de code.

Nous allons maintenant créer une instance du générateur à l'aide de la classe RandomStringGenerator.Builder . Bien entendu, 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 du caractère 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 de RandomStringGenerator est qu'il n'a pas la possibilité de spécifier le nombre de caractères dans chaque ensemble, comme dans 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()) 

Par défaut, RandomStringGenerator utilise ThreadLocalRandom pour le caractère aléatoire. Maintenant, il est important de mentionner que cela n'assure pas la sécurité cryptographique .

Cependant, nous pouvons définir la source du caractère aléatoire en utilisant usingRandom (TextRandomProvider). Par exemple, nous pouvons utiliser SecureTextRandomProvider 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 de RandomStringUtils

Une autre option que nous pourrions utiliser est la classe RandomStringUtils dans la bibliothèque Apache Commons Lang. Cette classe expose plusieurs méthodes statiques que nous pouvons utiliser pour notre déclaration 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()) 

Ici, RandomStringUtils utilise Random par défaut comme source du caractère aléatoire. 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());

Désormais, nous pourrions assurer la sécurité cryptographique en utilisant une instance de SecureRandom . Cependant, cette fonctionnalité ne peut pas être étendue à d'autres méthodes de la bibliothèque. Par ailleurs, Apache recommande l'utilisation de RandomStringUtils pour des cas d'utilisation simples uniquement.

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

Nous pouvons également utiliser la classe SecureRandom 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 que 33 et 45 désignent la plage de caractères Unicode. Maintenant, nous pouvons générer plusieurs flux selon nos besoins. Ensuite, nous pouvons fusionner les ensembles 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()) c = 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 disponibles à l'adresse over sur GitHub.

Fond Java

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS