Как закодировать пароль с помощью HMAC-SHA512 в Spring Boot Security

У меня есть старое приложение, работающее на PHP, которое использует функцию base64_encode(hash_hmac(“sha512”, $p_password, $p_salt, true)) для кодирования паролей в нашей базе данных. Я переношу это приложение на Java Spring Boot и хочу точно так же кодировать пароли во время аутентификации.

Я нашел, как сделать тот же метод хеширования с Java в этом сообщении Compute HMAC-SHA512 с секретным ключом в java, и я также узнал, что у нас может быть несколько кодировщиков паролей для старых и новых пользователей с https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-Release#constructing-delegatingpasswordencoder

Но я до сих пор не могу найти пример того, как я могу интегрировать этот метод хеширования в процесс аутентификации Spring. Мне нужно создать PasswordEncoder bean-компонент, и я не знаю, что поместить внутрь. Я пробовал Pbkdf2PasswordEncoder, потому что он может создать некоторый хэш SHA-512, как в моем приложении, но получаю сообщение об ошибке Detected a Non-hex character at 1 or 2 position. Вероятно, это связано с тем, что пароли не имеют префикса {pbkdf2} в базе данных. Следующий код - это то, что я сейчас использую как PasswordEncoder.

@Bean
public PasswordEncoder passwordEncoder() {
     Pbkdf2PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder("salt");
     passwordEncoder.setAlgorithm(Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA512);
     return passwordEncoder;
}

Мне нужна помощь в настройке правильного кодировщика паролей для использования HMAC-SHA512 в моем процессе аутентификации с помощью Java Spring, а во второй раз объедините его с BCrytPasswordEncoder (для новых пользователей) с DelegatingPasswordEncoder. Может быть, требуется обновить пароли в БД, чтобы указать в них правильный кодировщик?

Если мой вопрос недостаточно точен или отсутствует информация, спрашивайте меня, чтобы узнать подробности :)


person TCH    schedule 02.03.2021    source источник


Ответы (2)


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

@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {       
    
  @Bean
  public PasswordEncoder passwordEncoder() {
      Map<String, PasswordEncoder> encoders = new HashMap<>();  
    
    Pbkdf2PasswordEncoder bcryprPe = new Pbkdf2PasswordEncoder("salt");       
    bcryprPe.setAlgorithm(
       Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA512);
    encoders.put("pbkdf2", pbkdf2Pe);
    // add other PasswordEncoder here: 
    encoders.put("scrypt", new SCryptPasswordEncoder());
      return new DelegatingPasswordEncoder("pbkdf2", encoders);
  }
}

С участием

return new DelegatingPasswordEncoder("pbkdf2", encoders); 

мы говорим Spring Security: используйте pbkdf2 в качестве кодировщика паролей по умолчанию.

Если предоставленный хэш равен {scrypt} 12345, DelegatingPasswordEncoder делегирует SCryptPasswordEncoder, если префикса нет, приложение будет использовать значение по умолчанию.

person Meziane    schedule 02.03.2021
comment
Привет, я попробовал ваше решение, даже если я не думаю, что это действительно моя потребность. Я получаю следующее исключение. Нет PasswordEncoder, сопоставленного с нулевым идентификатором, потому что я думаю, что пароли в моей базе данных не имеют префикса {pbkdf2}. На самом деле, я не уверен, что Pbkdf2PasswordEncoder мне подходит, но я подумал, что он ближе всего воспроизводит тот же результат, что и мой метод base64_encode (hash_hmac («sha512», $ p_password, $ p_salt, true)) (в PHP) . Мне просто нужна конфигурация xxxPasswordEncoder для воспроизведения этого метода hasing, но, возможно, это невозможно - person TCH; 02.03.2021
comment
Для настройки нескольких PasswordEncoder, безусловно, можно использовать DelegatingPasswordEncoder. Другой вопрос, какой использовать PasswordEncoder. Попробуйте на первом этапе использовать ваш PasswordEncoder, а не использовать DelegatingPasswordEncoder для использования второго PasswordEncoder. - person Meziane; 03.03.2021
comment
Я знаю, как работает DelegatingPasswordEncoder. Мой вопрос касается только создания моего собственного PasswordEncoder или использования одного из Spring Security, который делает то же самое, что и base64_encode (hash_hmac («sha512», $ p_password, $ p_salt, true)) в PHP. - person TCH; 03.03.2021
comment
Вы знаете, как работает DelegatingPasswordEncoder? Я не это понял из вашего вопроса. В любом случае, похоже, вы решили свою проблему. Это цель stackoverflow: я только что попытался вам помочь. - person Meziane; 03.03.2021

Наконец-то я получил то, что хотел. Я создал реализацию PasswordEncoder, вдохновленную //github.com/lathspell/java_test/blob/master/java_test_openldap/src/main/java/LdapSha512PasswordEncoder.java

in WebSecurityConfig.java

@Bean
public PasswordEncoder passwordEncoder() {
        Map<String, PasswordEncoder> encoders = new HashMap<>();
        encoders.put("SSHA-512", new Hmac512PasswordEncoder("salt"));
        encoders.put("bcrypt", new BCryptPasswordEncoder());
        return new DelegatingPasswordEncoder("SSHA-512", encoders);
}

in Hmac512PasswordEncoder.java

public class Hmac512PasswordEncoder implements PasswordEncoder {

private static final String SSHA512_PREFIX = "{SSHA-512}";
private static final String HMAC_SHA512 = "HmacSHA512";

private final String salt;

public Hmac512PasswordEncoder(String salt) {
    if (salt == null) {
        throw new IllegalArgumentException("salt cannot be null");
    }
    this.salt = salt;
}

public String encode(CharSequence rawPassword) {
    String result = null;

    try {
        Mac sha512Hmac = Mac.getInstance(HMAC_SHA512);
        final byte[] byteKey = Utf8.encode(salt);
        SecretKeySpec keySpec = new SecretKeySpec(byteKey, HMAC_SHA512);
        sha512Hmac.init(keySpec);
        byte[] macData = sha512Hmac.doFinal(Utf8.encode(rawPassword.toString()));

        result = SSHA512_PREFIX + Base64.getEncoder().encodeToString(macData);
        //result = bytesToHex(macData);
    } catch (InvalidKeyException | NoSuchAlgorithmException e) {
        e.printStackTrace();
    }

    return result;
}

public boolean matches(CharSequence rawPassword, String encodedPassword) {
    if (rawPassword == null || encodedPassword == null) {
        return false;
    }

    String encodedRawPass = encode(rawPassword);

    return MessageDigest.isEqual(Utf8.encode(encodedRawPass), Utf8.encode(encodedPassword));
}
}
person TCH    schedule 03.03.2021