Может кто-нибудь объяснить, как BCrypt проверяет хеш? - PullRequest
26 голосов
/ 22 марта 2011

Я использую C # и BCrypt.Net для хэширования моих паролей.

Например:

string salt = BCrypt.Net.BCrypt.GenerateSalt(6);
var hashedPassword = BCrypt.Net.BCrypt.HashPassword("password", salt);

//This evaluates to True. How? I'm not telling it the salt anywhere, nor
//is it a member of a BCrypt instance because there IS NO BCRYPT INSTANCE.
Console.WriteLine(BCrypt.Net.BCrypt.Verify("password", hashedPassword));
Console.WriteLine(hashedPassword);

Как BCrypt проверяет пароль с помощью хэша, если соль нигде не сохраняется. Единственная идея, которая у меня есть, заключается в том, что она как-то добавляет соль в конце хэша.

Это правильное предположение?

Ответы [ 2 ]

85 голосов
/ 07 июня 2012

Хеш BCrypt строка выглядит следующим образом:

$2a$10$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm
$==$==$======================-------------------------------

Где

  • 2a: идентификатор алгоритма (BCrypt,Пароль в кодировке UTF8, нулевое окончание)
  • 10: коэффициент стоимости (2 10 = 1024 раунда)
  • Ro0CUfOqk6cXEKf3dyaM7O: соль OpenBSD-Base64 (22 символа, 16 байт)
  • hSCvnwM9s4wIX9JeLapehKK5YdLxKcm: хэш в кодировке OpenBSD-Base64 (31 символ, 24 байта)

Редактировать : я простозаметил, что эти слова подходят точно.Я должен был поделиться:

$2a$10$TwentytwocharactersaltThirtyonecharacterspasswordhash
$==$==$======================-------------------------------

BCrypt делает создает 24-байтовый двоичный хэш, используя 16-байтовую соль.Вы можете хранить бинарный хеш и соль так, как вам нравится;ничто не говорит, что у вас есть для кодирования base-64 в строку.

Но BCrypt был создан парнями, которые работали над OpenBSD. OpenBSD уже определяет формат файла паролей:

$ [HashAlgorithmIdentifier] $ [AlgorithmSpecificData]

Это означаетчто «спецификация bcrypt» неразрывно связана с форматом файла паролей OpenBSD.И всякий раз, когда кто-либо создает "bcrypt hash" , они всегда преобразуют его в строку ISO-8859-1 в формате:

$ 2a $ [Cost] $ [Base64Salt][Base64Hash]

Несколько важных моментов:

  • 2a - идентификатор алогитма
    • 1: MD5
    • 2: ранний bcrypt, из-за которого возникла путаница по поводу того, какие пароли кодирования находятся в (устаревшем)
    • 2a: текущий bcrypt, который указывает пароли как UTF-8закодированный
  • Стоимость - это коэффициент стоимости, используемый при вычислении хэша.Текущее значение равно 10, что означает, что внутренняя настройка ключа выполняется через 1024 раунда
    • 10: 2 10 = 1024 итерации
    • 11:2 11 = 2048 итераций
    • 12: 2 12 = 4096 итераций
  • алгоритм base64, используемыйфайл паролей OpenBSD отличается от кодировки Base64, которую используют все остальные ;у них есть свои собственные:

    Regular Base64 Alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
        BSD Base64 Alphabet: ./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
    

    Таким образом, любые реализации bcrypt не могут использовать любую встроенную или стандартную библиотеку base64


Вооруженные этим знаниемТеперь вы можете проверить пароль correctbatteryhorsestapler по сохраненному хешу:

$2a$12$mACnM5lzNigHMaf7O1py1O3vlf6.BA8k8x3IoJ.Tq3IB/2e7g61Km
26 голосов
/ 22 марта 2011

Как BCrypt проверяет пароль с помощью хеша, если соль нигде не сохраняется?

Очевидно, что он не делает ничего подобного.Соль должна быть где-то сохранена.

Давайте посмотрим схемы шифрования паролей в Википедии.From http://en.wikipedia.org/wiki/Crypt_(Unix):

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

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

    StringBuilder rs = new StringBuilder();
    rs.Append("$2");
    if (minor >= 'a') {
        rs.Append(minor);
    }
    rs.Append('$');
    if (rounds < 10) {
        rs.Append('0');
    }
    rs.Append(rounds);
    rs.Append('$');
    rs.Append(EncodeBase64(saltBytes, saltBytes.Length));
    rs.Append(EncodeBase64(hashed,(bf_crypt_ciphertext.Length * 4) - 1));
    return rs.ToString();

Ясно, что возвращаемая строка является информацией о версии, за которой следует число использованных раундов, за которым следует соль, закодированная как base64, за которой следует хэш, закодированный как base64.

...