Генерирайте сигурна случайна парола в Java

Java Top

Току що обявих новия курс Learn Spring , фокусиран върху основите на Spring 5 и Spring Boot 2:

>> ПРЕГЛЕД НА КУРСА

1. Въведение

В този урок ще разгледаме различни методи, които можем да използваме за генериране на сигурна случайна парола в Java.

В нашите примери ще генерираме десет символни пароли, всяка с минимум два малки букви, два главни символа, две цифри и два специални знака.

2. Използване на Passay

Passay е библиотека за налагане на правила за пароли. За отбелязване е, че можем да използваме библиотеката, за да генерираме паролата, използвайки конфигурируем набор от правила.

С помощта на реализациите на CharacterData по подразбиране можем да формулираме правилата, необходими за паролата. Освен това можем да формулираме персонализирани реализации на CharacterData, които да отговарят на нашите изисквания :

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

Тук създадохме персонализирана реализация на CharacterData за специални символи. Това ни позволява да ограничим набора от валидни разрешени символи.

Отделно от това, ние използваме реализациите по подразбиране на CharacterData за другите ни правила.

Сега, нека проверим нашия генератор спрямо единичен тест. Например можем да проверим наличието на два специални знака:

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

Струва си да се отбележи, че въпреки че Passay е с отворен код, той е двойно лицензиран както под LGPL, така и от Apache 2 . Както при всеки софтуер на трети страни, трябва да сме сигурни, че спазваме тези лицензи, когато го използваме в нашите продукти. Уебсайтът на GNU съдържа повече информация за LGPL и Java.

3. Използване на RandomStringGenerator

След това нека разгледаме RandomStringGenerator в Apache Commons Text. С RandomStringGenerator можем да генерираме Unicode низове, съдържащи посочения брой кодови точки.

Сега ще създадем екземпляр на генератора, като използваме класа RandomStringGenerator.Builder . Разбира се, можем също така да манипулираме допълнително свойствата на генератора.

С помощта на конструктора можем лесно да променим внедряването по подразбиране на случайността. Освен това можем да определим и символите, които са разрешени в низа:

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

Сега, едно ограничение на използването на RandomStringGenerator е, че му липсва способността да се определя броят на символите във всеки набор, като в Passay. Въпреки това можем да заобиколим това, като обединим резултатите от множество набори:

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

След това нека проверим генерираната парола, като проверим малките букви:

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

По подразбиране RandomStringGenerator използва ThreadLocalRandom за произволност. Сега е важно да споменем, че това не гарантира криптографска сигурност .

Въпреки това, можем да зададем източника на случайност, използвайки usingRandom (TextRandomProvider). Например можем да използваме SecureTextRandomProvider за криптографска сигурност:

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. Използване на RandomStringUtils

Друг вариант, който бихме могли да използваме, е класът RandomStringUtils в библиотеката на Apache Commons Lang. Този клас излага няколко статични метода, които можем да използваме за нашето твърдение за проблем.

Нека да видим как можем да предоставим диапазона от кодови точки, които са приемливи за паролата:

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

За да проверим генерираната парола, нека проверим броя на цифровите знаци:

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

Тук RandomStringUtils използва Random по подразбиране като източник на произволност. В библиотеката обаче има метод, който ни позволява да посочим източника на случайност:

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

Сега бихме могли да осигурим криптографска сигурност, като използваме екземпляр на SecureRandom . Тази функционалност обаче не може да бъде разширена до други методи в библиотеката. Като странична бележка Apache се застъпва за използването на RandomStringUtils само за случаи на проста употреба.

5. Използване на метод на персонализирана помощна програма

Също така можем да използваме класа SecureRandom, за да създадем потребителски клас на помощната програма за нашия сценарий. Като начало, нека генерираме низ от специални знаци с дължина две:

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

Също така обърнете внимание, че 33 и 45 означават обхвата на Unicode символи. Сега можем да генерираме множество потоци според нашите изисквания. След това можем да обединим наборите от резултати, за да генерираме необходимата парола:

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

Сега, нека проверим генерираната парола за броя на специалните знаци:

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

6. Заключение

В този урок успяхме да генерираме пароли, отговарящи на нашите изисквания, използвайки различни библиотеки.

Както винаги, примерните кодове, използвани в статията, са достъпни в GitHub.

Дъно на Java

Току що обявих новия курс Learn Spring , фокусиран върху основите на Spring 5 и Spring Boot 2:

>> ПРЕГЛЕД НА КУРСА