Genereer een veilig willekeurig wachtwoord in Java

Java Top

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS

1. Inleiding

In deze zelfstudie bekijken we verschillende methoden die we kunnen gebruiken om een ​​veilig willekeurig wachtwoord in Java te genereren.

In onze voorbeelden genereren we wachtwoorden van tien tekens, elk met minimaal twee kleine letters, twee hoofdletters, twee cijfers en twee speciale tekens.

2. Passay gebruiken

Passay is een bibliotheek voor het afdwingen van wachtwoordbeleid. We kunnen met name gebruik maken van de bibliotheek om het wachtwoord te genereren met behulp van een configureerbare regelset.

Met behulp van de default CharacterData implementaties, kunnen we de regels formuleren die nodig zijn voor het wachtwoord. Bovendien kunnen we formuleren op maat CharacterData implementaties om aan onze eisen te voldoen:

openbare String GenereerPassayPassword () {PasswordGenerator gen = nieuwe PasswordGenerator (); CharacterData lowerCaseChars = EnglishCharacterData.LowerCase; CharacterRule lowerCaseRule = nieuwe CharacterRule (lowerCaseChars); lowerCaseRule.setNumberOfCharacters (2); CharacterData upperCaseChars = EnglishCharacterData.UpperCase; CharacterRule upperCaseRule = nieuwe CharacterRule (upperCaseChars); upperCaseRule.setNumberOfCharacters (2); CharacterData digitChars = EnglishCharacterData.Digit; CharacterRule digitRule = nieuwe CharacterRule (digitChars); digitRule.setNumberOfCharacters (2); CharacterData specialChars = nieuwe CharacterData () {openbare String getErrorCode () {terug ERROR_CODE; } public String getCharacters () {return "[email protected] # $% ^ & * () _ +"; }}; CharacterRule splCharRule = nieuwe CharacterRule (specialChars); splCharRule.setNumberOfCharacters (2); String wachtwoord = gen.generatePassword (10, splCharRule, lowerCaseRule, upperCaseRule, digitRule); wachtwoord teruggeven; }

Hier hebben we een aangepast CharacterData implementatie voor speciale karakters. Dit stelt ons in staat om de toegestane reeks geldige tekens te beperken.

Afgezien daarvan maken we gebruik van standaardimplementaties van CharacterData voor onze andere regels.

Laten we nu onze generator vergelijken met een unit-test. We kunnen bijvoorbeeld de aanwezigheid van twee speciale tekens controleren:

@Test openbare leegte whenPasswordGeneratedUsingPassay_thenSuccessful () {RandomPasswordGenerator passGen = nieuwe RandomPasswordGenerator (); String wachtwoord = passGen.generatePassayPassword (); int specialCharCount = 0; for (char c: password.toCharArray ()) if (c> = 33 

Het is vermeldenswaard dat hoewel Passay open source is, heeft het een dubbele licentie onder zowel LGPL als Apache 2. Zoals met alle software van derden, moeten we ervoor zorgen dat we aan deze licenties voldoen wanneer we deze in onze producten gebruiken. De GNU-website heeft meer informatie over de LGPL en Java.

3. Met behulp van RandomStringGenerator

Laten we vervolgens eens kijken naar het RandomStringGenerator in Apache Commons-tekst. Met RandomStringGenerator, we kunnen Unicode-strings genereren die het opgegeven aantal codepunten bevatten.

Nu gaan we een instantie van de generator maken met behulp van de RandomStringGenerator.Builder klasse. Natuurlijk kunnen we ook de eigenschappen van de generator verder manipuleren.

Met behulp van de builder kunnen we eenvoudig de standaardimplementatie van willekeur wijzigen. Bovendien kunnen we ook de tekens definiëren die in de string zijn toegestaan:

public String generationRandomSpecialCharacters (int lengte) {RandomStringGenerator pwdGenerator = nieuwe RandomStringGenerator.Builder (). WithinRange (33, 45) .build (); retourneer pwdGenerator.generate (lengte); } 

Nu, een beperking van het gebruik RandomStringGenerator is dat het mist de mogelijkheid om het aantal karakters in elke set te specificeren, zoals in Passay. We kunnen dat echter omzeilen door de resultaten van meerdere sets samen te voegen:

openbare String GenereerCommonTextPassword () {String pwString = GenereerRandomSpecialCharacters (2) .concat (GenereerRandomNummers (2)) .concat (GenereerRandomAlphabet (2, true)) .concat (GenereerRandomAlphabet (2, false)) .concat (GenereerRandomCharacters (2)); Lijst pwChars = pwString.chars () .mapToObj (data -> (char) data) .collect (Collectors.toList ()); Collections.shuffle (pwChars); String wachtwoord = pwChars.stream () .collect (StringBuilder :: nieuw, StringBuilder :: append, StringBuilder :: append) .toString (); wachtwoord teruggeven; }

Laten we vervolgens het gegenereerde wachtwoord valideren door de kleine letters te verifiëren:

@Test openbare leegte whenPasswordGeneratedUsingCommonsText_thenSuccessful () {RandomPasswordGenerator passGen = nieuwe RandomPasswordGenerator (); String wachtwoord = passGen.generateCommonTextPassword (); int lowerCaseCount = 0; for (char c: password.toCharArray ()) 

Standaard, RandomStringGenerator maakt gebruik van ThreadLocalRandom voor willekeur. Nu is het belangrijk om te vermelden dat dit geen cryptografische beveiliging garandeert.

We kunnen echter de bron van willekeur instellen met usingRandom (TextRandomProvider). We kunnen er bijvoorbeeld gebruik van maken SecureTextRandomProvider voor cryptografische beveiliging:

openbare String GenereerRandomSpecialCharacters (int lengte) {SecureTextRandomProvider stp = nieuwe SecureTextRandomProvider (); RandomStringGenerator pwdGenerator = nieuwe RandomStringGenerator.Builder () .withinRange (33, 45) .usingRandom (stp) .build (); retourneer pwdGenerator.generate (lengte); }

4. Met behulp van RandomStringUtils

Een andere optie die we zouden kunnen gebruiken, is de RandomStringUtils klasse in de Apache Commons Lang Library. Deze klasse legt verschillende statische methoden bloot die we kunnen gebruiken voor onze probleemstelling.

Laten we eens kijken hoe we het bereik van codepunten kunnen bieden dat acceptabel is voor het wachtwoord:

 openbare String genererenCommonLangPassword () {String upperCaseLetters = RandomStringUtils.random (2, 65, 90, waar, waar); String lowerCaseLetters = RandomStringUtils.random (2, 97, 122, true, true); Tekenreeksnummers = RandomStringUtils.randomNumeric (2); String specialChar = RandomStringUtils.random (2, 33, 47, false, false); String totalChars = RandomStringUtils.randomAlphanumeric (2); String combinedChars = upperCaseLetters.concat (lowerCaseLetters) .concat (getallen) .concat (specialChar) .concat (totalChars); Lijst pwdChars = combinedChars.chars () .mapToObj (c -> (char) c) .collect (Collectors.toList ()); Collections.shuffle (pwdChars); String wachtwoord = pwdChars.stream () .collect (StringBuilder :: nieuw, StringBuilder :: append, StringBuilder :: append) .toString (); wachtwoord teruggeven; }

Laten we het aantal numerieke tekens verifiëren om het gegenereerde wachtwoord te valideren:

@Test openbare leegte whenPasswordGeneratedUsingCommonsLang3_thenSuccessful () {RandomPasswordGenerator passGen = nieuwe RandomPasswordGenerator (); String wachtwoord = passGen.generateCommonsLang3Password (); int numCount = 0; for (char c: password.toCharArray ()) 

Hier, RandomStringUtils maakt gebruik van Willekeurig standaard als de bron van willekeur. Er is echter een methode in de bibliotheek waarmee we de bron van willekeur kunnen specificeren:

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

Nu kunnen we zorgen voor cryptografische beveiliging met behulp van een instantie van SecureRandom. Deze functionaliteit kan echter niet worden uitgebreid naar andere methoden in de bibliotheek. Even terzijde, Apache pleit voor het gebruik van RandomStringUtils alleen voor eenvoudige gebruikssituaties.

5. Met behulp van een aangepaste hulpprogramma-methode

We kunnen ook gebruik maken van de SecureRandom class om een ​​aangepaste utility-klasse voor ons scenario te maken. Laten we om te beginnen een reeks speciale tekens met een lengte van twee genereren:

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

Merk dat ook op 33 en 45 geven het bereik van Unicode-tekens aan. Nu kunnen we meerdere streams genereren volgens onze vereisten. Vervolgens kunnen we de resultatensets samenvoegen om het vereiste wachtwoord te genereren:

openbare String GenereerSecureRandomPassword () {Stream pwdStream = Stream.concat (getRandomNumbers (2), Stream.concat (getRandomSpecialChars (2), Stream.concat (getRandomAlphabets (2, true), getRandomAlphabets (4, false)))); Lijst charList = pwdStream.collect (Collectors.toList ()); Collections.shuffle (charList); String wachtwoord = charList.stream () .collect (StringBuilder :: nieuw, StringBuilder :: append, StringBuilder :: append) .toString (); wachtwoord teruggeven; } 

Laten we nu het gegenereerde wachtwoord valideren voor het aantal speciale tekens:

@Test openbare leegte whenPasswordGeneratedUsingSecureRandom_thenSuccessful () {RandomPasswordGenerator passGen = nieuwe RandomPasswordGenerator (); String wachtwoord = passGen.generateSecureRandomPassword (); int specialCharCount = 0; for (char c: password.toCharArray ()) c = 2); 

6. Conclusie

In deze tutorial hebben we wachtwoorden kunnen genereren, die voldoen aan onze vereisten, met behulp van verschillende bibliotheken.

Zoals altijd zijn de codevoorbeelden die in het artikel worden gebruikt, beschikbaar op GitHub.

Java onderkant

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS