2016-08-23 2 views
3

У меня есть старое приложение на базе Symfony2, и я разрабатываю замену Dropwizard на Java. Я перенес все записи пользователя из старой БД в новую Datamodel. Я также добавил новые поля для паролей и импортировал старый пароль и солевые поля.Создание таких же хэшей, как symfony2, с помощью java (SHA-512)

Теперь я хочу сделать хорошо известную процедуру. Позвольте пользователю войти в систему, попробуйте новое поле пароля. Если это не удается, попробуйте перенесенные, если они работают, закодируйте пароль cleartext с новым алгоритмом и сохраните новый хеш в новом поле pasword. Чтобы пользователи переносили хэши паролей из старой процедуры в новую.

Звучит просто и нормально, это работает как обычно, но этот Symfony и PHP меня смущает.

Где я застрял, нужно создать тот же хэш с java, что и symfony. Старое приложение использует MessageDigestPasswordEncoder с "SHA512", кодирование base64 и 5000 итераций, все значения по умолчанию;)

Важными методами являются:

MessageDigestPasswordEncoder:

public function encodePassword($raw, $salt) { 
    if ($this->isPasswordTooLong($raw)) { 
    throw new BadCredentialsException('Invalid password.'); 
    } 

    if (!in_array($this->algorithm, hash_algos(), true)) { 
    throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm)); 
    } 

    $salted = $this->mergePasswordAndSalt($raw, $salt); 
    $digest = hash($this->algorithm, $salted, true); 

    // "stretch" hash 
    for ($i = 1; $i < $this->iterations; ++$i) { 
    $digest = hash($this->algorithm, $digest.$salted, true); 
    } 

    return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest); 
} 

И BasePasswordEncoder:

protected function mergePasswordAndSalt($password, $salt) { 
    if (empty($salt)) { 
    return $password; 
    } 

    if (false !== strrpos($salt, '{') || false !== strrpos($salt, '}')) { 
    throw new \InvalidArgumentException('Cannot use { or } in salt.'); 
    } 

    return $password.'{'.$salt.'}'; 
} 

Это кажется прямым, но я придерживался его. Как я прочитал это делает:

  1. Объединить соли и пароль открытым текстом для: «пароль {соль}»
  2. Hash это строка с SHA-512 и возвращает двоичную строку в переваривать переменную
  3. итерацию 5k раза и использовать дайджест сцепляется с объединенном читаемых паролями перефразировать в дайджесте
  4. закодировать дайджест base64

Так вот моя попытка в Java:

import org.bouncycastle.jce.provider.BouncyCastleProvider; 
import org.slf4j.Logger; 
import java.io.UnsupportedEncodingException; 
import java.security.MessageDigest; 
import java.security.NoSuchAlgorithmException; 
import java.util.Base64; 

public void legacyEncryption(String salt, String clearPassword) throws UnsupportedEncodingException, NoSuchAlgorithmException { 
    // Get digester instance for algorithm "SHA-512" using BounceCastle 
    MessageDigest digester = MessageDigest.getInstance("SHA-512", new BouncyCastleProvider()); 

    // Create salted password string 
    String mergedPasswordAndSalt = clearPassword + "{" + salt + "}"; 

    // First time hash the input string by using UTF-8 encoded bytes. 
    byte[] hash = digester.digest(mergedPasswordAndSalt.getBytes("UTF-8")); 

    // Loop 5k times 
    for (int i = 0; i < 5000; i++) { 
    // Concatenate the hash bytes with the clearPassword bytes and rehash 
    hash = digester.digest(ArrayUtils.addAll(hash, mergedPasswordAndSalt.getBytes("UTF-8"))); 
    } 

    // Log the resulting hash as base64 String 
    logger.info("Legace password digest: salt=" + salt + " hash=" + Base64.getEncoder().encodeToString(hash)); 
} 

Кто-нибудь видит проблему? Я думаю, что разница в результате: PHP: binary.binary и JAVA: addAll (байт [], байт [])

Заранее спасибо

ответ

3

Реализация на стороне РНР правильно выполнив 5k итераций, выполнив первый раунд хэширования, а затем зациклив 4999 раз.

$digest = hash($this->algorithm, $salted, true); 

for ($i = 1; $i < $this->iterations; ++$i) { 
    $digest = hash($this->algorithm, $digest.$salted, true); 
} 

В реализации java цикл for начинается с 0, что приводит к 5k + 1 итерации.

Запустив цикл for в 1 в java, результирующие хэши-данные затем равны.

byte[] hash = digester.digest(mergedPasswordAndSalt.getBytes("UTF-8")); 

for (int i = 0; i < 5000; i++) { 
    hash = digester.digest(ArrayUtils.addAll(hash, mergedPasswordAndSalt.getBytes("UTF-8"))); 
} 
+0

Звучит неплохо и делает выделение смысла;) Я дам ему попробовать завтра.Только сейчас закончил писать сегодня –

+0

В PHP-коде, если '$ this-> iterations'' '5000 и' $ i' инициализируется '1' в цикле for, тогда цикл for будет перебирать' 4,999' раз, не '5000 раз. И так как вы говорите, что первый раунд выполняется за пределами цикла в PHP-коде, то и PHP, и Java-код должны повторять «5000 раз». –

+0

Вы правы, PHP-код выполняет итерацию 5000 (4999 + 1) раз. Однако код Java выполняет итерацию 5001 раз (5000 + 1), так как он также имеет начальный раунд вне цикла. Я смутил оба языка в своем ответе, извините. Я исправлю это. Я все еще думаю, что это корень проблемы. –

Смежные вопросы