Genera una password casuale sicura in Java

Java Top

Ho appena annunciato il nuovo corso Learn Spring , incentrato sui fondamenti di Spring 5 e Spring Boot 2:

>> SCOPRI IL CORSO

1. Introduzione

In questo tutorial, esamineremo vari metodi che possiamo utilizzare per generare una password casuale sicura in Java.

Nei nostri esempi, genereremo password di dieci caratteri, ciascuna con un minimo di due caratteri minuscoli, due caratteri maiuscoli, due cifre e due caratteri speciali.

2. Utilizzo di Passay

Passay è una libreria per l'applicazione delle policy delle password. In particolare, possiamo utilizzare la libreria per generare la password utilizzando un set di regole configurabile.

Con l'aiuto delle implementazioni predefinite di CharacterData , possiamo formulare le regole richieste per la password. Inoltre, possiamo formulare implementazioni personalizzate di CharacterData per soddisfare le nostre esigenze :

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

Qui, abbiamo creato un'implementazione personalizzata di CharacterData per i caratteri speciali. Questo ci consente di limitare il set di caratteri validi consentiti.

Oltre a questo, stiamo utilizzando le implementazioni predefinite di CharacterData per le nostre altre regole.

Ora, controlliamo il nostro generatore con un test unitario. Ad esempio, possiamo verificare la presenza di due caratteri speciali:

@Test public void whenPasswordGeneratedUsingPassay_thenSuccessful() { RandomPasswordGenerator passGen = new RandomPasswordGenerator(); String password = passGen.generatePassayPassword(); int specialCharCount = 0; for (char c : password.toCharArray()) if (c >= 33 

Vale la pena notare che sebbene Passay sia open source, ha una doppia licenza sia con LGPL che con Apache 2 . Come con qualsiasi software di terze parti, dobbiamo assicurarci di rispettare queste licenze quando lo utilizziamo nei nostri prodotti. Il sito web GNU contiene ulteriori informazioni su LGPL e Java.

3. Utilizzo di RandomStringGenerator

Successivamente, diamo un'occhiata a RandomStringGenerator in Apache Commons Text. Con RandomStringGenerator, possiamo generare stringhe Unicode contenenti il ​​numero specificato di code point.

Ora creeremo un'istanza del generatore utilizzando la classe RandomStringGenerator.Builder . Ovviamente possiamo anche manipolare ulteriormente le proprietà del generatore.

Con l'aiuto del builder, possiamo facilmente modificare l'implementazione predefinita della casualità. Inoltre, possiamo anche definire i caratteri consentiti nella stringa:

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

Ora, una limitazione dell'utilizzo di RandomStringGenerator è che non ha la possibilità di specificare il numero di caratteri in ogni set, come in Passay. Tuttavia, possiamo aggirarlo unendo i risultati di più insiemi:

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

Successivamente, convalidiamo la password generata verificando le lettere minuscole:

@Test public void whenPasswordGeneratedUsingCommonsText_thenSuccessful() { RandomPasswordGenerator passGen = new RandomPasswordGenerator(); String password = passGen.generateCommonTextPassword(); int lowerCaseCount = 0; for (char c : password.toCharArray()) 

Per impostazione predefinita, RandomStringGenerator utilizza ThreadLocalRandom per la casualità. Ora, è importante ricordare che questo non garantisce la sicurezza crittografica .

Tuttavia, possiamo impostare l'origine della casualità utilizzando usingRandom (TextRandomProvider). Ad esempio, possiamo utilizzare SecureTextRandomProvider per la sicurezza crittografica:

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. Utilizzo di RandomStringUtils

Un'altra opzione che potremmo utilizzare è la classe RandomStringUtils nella libreria Apache Commons Lang. Questa classe espone diversi metodi statici che possiamo usare per la nostra dichiarazione del problema.

Vediamo come possiamo fornire l'intervallo di punti di codice accettabili per la password:

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

Per convalidare la password generata, verifichiamo il numero di caratteri numerici:

@Test public void whenPasswordGeneratedUsingCommonsLang3_thenSuccessful() { RandomPasswordGenerator passGen = new RandomPasswordGenerator(); String password = passGen.generateCommonsLang3Password(); int numCount = 0; for (char c : password.toCharArray()) 

In questo caso, RandomStringUtils utilizza Random per impostazione predefinita come fonte di casualità. Tuttavia, esiste un metodo all'interno della libreria che ci consente di specificare l'origine della casualità:

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

Ora, possiamo garantire la sicurezza crittografica utilizzando un'istanza di SecureRandom . Tuttavia, questa funzionalità non può essere estesa ad altri metodi nella libreria. In una nota a margine , Apache sostiene l'uso di RandomStringUtils solo per casi d'uso semplici.

5. Utilizzo di un metodo di utilità personalizzato

Possiamo anche utilizzare la classe SecureRandom per creare una classe di utilità personalizzata per il nostro scenario. Per i principianti, generiamo una stringa di caratteri speciali di lunghezza due:

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

Inoltre, si noti che 33 e 45 indicano l'intervallo di caratteri Unicode. Ora possiamo generare più flussi secondo i nostri requisiti. Quindi possiamo unire i set di risultati per generare la password richiesta:

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

Ora convalidiamo la password generata per il numero di caratteri speciali:

@Test public void whenPasswordGeneratedUsingSecureRandom_thenSuccessful() { RandomPasswordGenerator passGen = new RandomPasswordGenerator(); String password = passGen.generateSecureRandomPassword(); int specialCharCount = 0; for (char c : password.toCharArray()) c = 2); 

6. Conclusione

In questo tutorial, siamo stati in grado di generare password, conformi ai nostri requisiti, utilizzando diverse librerie.

Come sempre, gli esempi di codice utilizzati nell'articolo sono disponibili su GitHub.

Fondo Java

Ho appena annunciato il nuovo corso Learn Spring , incentrato sui fondamenti di Spring 5 e Spring Boot 2:

>> SCOPRI IL CORSO