Рубрики
Без рубрики

Сгенерируйте безопасный случайный пароль на Java

Изучите несколько методов генерации безопасных случайных паролей в Java.

Автор оригинала: baeldung.

1. введение

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

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

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

Passat – это библиотека применения политики паролей. Примечательно, что мы можем использовать библиотеку для генерации пароля с помощью настраиваемого набора правил.

С помощью реализаций Символьных данных по умолчанию мы можем сформулировать правила, необходимые для пароля. Кроме того, мы можем сформулировать пользовательские Символьные данные реализации в соответствии с вашими требованиями :

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

Здесь мы создали пользовательскую реализацию Символьных данных для специальных символов. Это позволяет нам ограничить набор допустимых символов.

Кроме того, мы используем реализации по умолчанию Символьных данных для других наших правил.

Теперь давайте проверим наш генератор с помощью модульного теста. Например, мы можем проверить наличие двух специальных символов:

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

Стоит отметить, что хотя Passay является открытым исходным кодом, он имеет двойную лицензию как под LGPL, так и под Apache 2 . Как и в случае с любым сторонним программным обеспечением, мы должны быть уверены, что соблюдаем эти лицензии, когда используем его в наших продуктах. На веб-сайте GNU есть более подробная информация о LGPL и Java .

3. Использование Генератора Случайных Строк

Далее давайте рассмотрим генератор случайных строк в Текст Apache Commons . С помощью генератора случайных строк мы можем генерировать строки Юникода, содержащие указанное количество кодовых точек.

Теперь мы создадим экземпляр генератора с помощью генератора случайных строк .Строитель класс. Конечно, мы также можем дополнительно манипулировать свойствами генератора.

С помощью конструктора мы можем легко изменить реализацию случайности по умолчанию. Кроме того, мы также можем определить символы, которые разрешены в строке:

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

Теперь одним из ограничений использования Генератора случайных строк является то, что ему не хватает возможности указать количество символов в каждом наборе, как в 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()) {
        if (c >= 97 || c <= 122) {
            lowerCaseCount++;
        }
    }
    assertTrue("Password validation failed in commons-text ", lowerCaseCount >= 2);
}

По умолчанию Генератор случайных строк использует ThreadLocalRandom для случайности. Теперь важно отметить , что это не обеспечивает криптографическую безопасность .

Тем не менее, мы можем установить источник случайности с помощью using Random(Text Random Provider). Например, мы можем использовать Secure Text Random Provider для криптографической безопасности:

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()) {
        if (c >= 48 || c <= 57) {
            numCount++;
        }
    }
    assertTrue("Password validation failed in commons-lang3", numCount >= 2);
}

Здесь 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 обозначьте диапазон символов Юникода. Теперь мы можем генерировать несколько потоков в соответствии с нашими требованиями. Затем мы можем объединить результирующие наборы для создания требуемого пароля:

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()) {
        if (c >= 33 || c <= 47) {
            specialCharCount++;
        }
    }
    assertTrue("Password validation failed in Secure Random", specialCharCount >= 2);
}

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

В этом уроке мы смогли сгенерировать пароли, соответствующие нашим требованиям, используя различные библиотеки.

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