Как получить такое же значение в php, как и encodePassword в инфраструктуре безопасности Spring? - PullRequest
0 голосов
/ 12 мая 2019

Мне нужно получить из php то же значение, что и результат, полученный с помощью метода encodePassword объекта Spring Security ShaPasswordEncoder.

У меня нет разрешения на изменение соответствующего Java-кода, поэтому только PHP должен решить эту проблему.

Java-код:

ShaPasswordEncoder shaPasswordEncoder = new ShaPasswordEncoder(256);
shaPasswordEncoder.setIterations(1000);
String password = "123456789";
String salt = "123456";
String hash2 = shaPasswordEncoder.encodePassword(password, salt);
System.out.println(hash2);

Я попробовал следующее:

$password = "123456789"; 
$salt = "123456";

$hash = hash_pbkdf2('sha256', $password, $salt, 1000);
echo 'hash_pbkdf2 result : '.$hash.'<br>';

$out = $salt.$password;
for ($i = 0; $i < 1000; $i++) $out = hash('sha256', $out);
echo 'hash result : '.$out.'<br>';

Java Результат:

9379d5a5fdd899cdf588b075988b9e94d2b3fe5cb44e593dd4a241f4757c8eec

php Результат:

hash_pbkdf2 result : 2794f28c44aa958663637ebeed391b533854fb2f4b7b7d09f521de8c1fe5c6bb
hash result : b0782b680af7a13aeffc61006d2799d69ddc0465c33f376b40d946dd232f62ed

Я хочу сделать результирующее значение php равным результирующему значению Java. Что мне делать?

1 Ответ

0 голосов
/ 12 мая 2019

Это процесс, который я прошел, чтобы понять это; Я напишу это полностью здесь, чтобы помочь вам изучить процесс, а не только это одно решение.

Из источника ShaPasswordEncoder (комментарии для краткости удалены):

public class ShaPasswordEncoder extends MessageDigestPasswordEncoder {
    public ShaPasswordEncoder() {
        this(1);
    }

    public ShaPasswordEncoder(int strength) {
        super("SHA-" + strength);
    }
}

Выглядит для меня как довольно тонкий класс-фантик. Вызов new ShaPasswordEncoder(256) фактически аналогичен вызову new MessageDigestPasswordEncoder("SHA-256"), поэтому давайте посмотрим на источник для , который :

public class MessageDigestPasswordEncoder extends BaseDigestPasswordEncoder {
    private final String algorithm;
    private int iterations = 1;

    [...]

    public String encodePassword(String rawPass, Object salt) {
        String saltedPass = mergePasswordAndSalt(rawPass, salt, false);   /* 1 */

        MessageDigest messageDigest = getMessageDigest();

        byte[] digest = messageDigest.digest(Utf8.encode(saltedPass));    /* 2 */

        // "stretch" the encoded value if configured to do so
        for (int i = 1; i < this.iterations; i++) {                       /* 3 */
            digest = messageDigest.digest(digest);
        }

        if (getEncodeHashAsBase64()) {
            return Utf8.decode(Base64.encode(digest));
        } else {
            return new String(Hex.encode(digest));                        /* 4 */
        }
    }

    [...]
}

Ах, теперь мы куда-то добираемся. Там было несколько дополнительных методов, но пока мы действительно заботимся только о encodePassword. Это работает примерно так, как вы ожидаете. Из пронумерованных строк:

  1. соль пароль
    • При этом используется невинный метод mergePasswordAndSalt, который мы рассмотрим в следующем
  2. Hash соленый пароль
    • Обратите внимание на кодировку UTF-8 здесь - это не всегда будет по умолчанию
  3. Неоднократно хэшируйте вывод для дополнительных итераций
    • Обратите внимание, что как вход, так и выход метода messageDigest.digest являются необработанными байтовыми массивами, а не шестнадцатеричными строками
  4. Возвращает шестнадцатеричную строку конечного результата

Как и было обещано, вот источник mergePasswordAndSalt метода:

protected String mergePasswordAndSalt(String password, Object salt, boolean strict) {
    if (password == null) {                                                              /* 1 */
        password = "";
    }

    if (strict && (salt != null)) {                                                      /* 2 */
        if ((salt.toString().lastIndexOf("{") != -1)
                || (salt.toString().lastIndexOf("}") != -1)) {
            throw new IllegalArgumentException("Cannot use { or } in salt.toString()");
        }
    }

    if ((salt == null) || "".equals(salt)) {
        return password;
    } else {
        return password + "{" + salt.toString() + "}";                                   /* 3 */
    }
}

Опять из пронумерованных разделов:

  1. Проверить пустой пароль
  2. Проверить наличие символов { и }; они не разрешены из-за следующего пункта
  3. Вывести соленый пароль в виде password{salt}

Здесь мы видим, почему ваша попытка воспроизвести этот алгоритм в PHP не удалась.

Из этой информации узнайте, можете ли вы создать функцию PHP, которая ведет себя таким же образом. Сегодня вечером я отредактирую полное PHP-решение, когда у меня будет доступ к среде для его тестирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...