Использование PBKDF2 с библиотекой OpenSSL
Я хочу использовать алгоритм PBKDF2 с SHA1 HMAC (основываясь на этом ответе).
Как я могу использовать это через библиотеку шифрования?
Я начал с того, что посмотрел на man openssl
, но openssl passwd
Команда ( страница man) поддерживает только небольшое количество алгоритмов. Глядя на крипто- документацию, модуль evp имеет метод EVP_BytesToKey.
Тщательный выбор параметров обеспечит реализацию, совместимую с PKCS#5 PBKDF1. Однако новые приложения обычно не должны использовать это (предпочитая, например, PBKDF2 из PCKS#5).
Что возвращает меня к моему первоначальному вопросу, как я могу использовать PBKDF2 через криптографию? Нужно ли копаться в коде и вызывать метод без использования API (например, PKCS5_PBKDF2_HMAC)? (и если так, что мешает этому быть разоблаченным?)
1 ответ
У меня есть работающий, но плохой пример C PBKDF2 через библиотеки OpenSSL в моем репозитории github, включая сценарии для компиляции под Linux и Windows (через MinGW). Исходный код, расположенный в разделе "Релизы", известен как хороший; Исходный код в ветке master - это WIP. Этот вариант лицензируется под тем же BSD с 4 пунктами в дополнение к лицензии SSLeay OpenSSL.
Я все еще работаю над добавлением нескольких функций, затем я вернусь к отличным данным, полученным на сайте StackExchange для Code Review, и обновлю их до синтаксиса C99 и так далее.
Базовый код очень примитивен и может содержать недостатки, несмотря на прохождение очень обширных тестовых векторов на основе строк. Он не был (пока) проверен на чистый двоичный ввод.
#include <openssl/evp.h>
#include <openssl/sha.h>
// crypto.h used for the version
#include <openssl/crypto.h>
void PBKDF2_HMAC_SHA_1nat_string(const char* pass, const unsigned char* salt, int32_t iterations, uint32_t outputBytes, char* hexResult)
{
unsigned int i;
unsigned char digest[outputBytes];
PKCS5_PBKDF2_HMAC_SHA1(pass, strlen(pass), salt, strlen(salt), iterations, outputBytes, digest);
for (i = 0; i < sizeof(digest); i++)
sprintf(hexResult + (i * 2), "%02x", 255 & digest[i]);
}
Если у вас есть 64-битная система, я бы настоятельно рекомендовал перейти на PBKDF2-HMAC-SHA-512 или PBKDF2-HMAC-SHA-384:
#include <openssl/evp.h>
#include <openssl/sha.h>
// crypto.h used for the version
#include <openssl/crypto.h>
void PBKDF2_HMAC_SHA_384_string(const char* pass, const unsigned char* salt, int32_t iterations, uint32_t outputBytes, char* hexResult)
{
unsigned int i;
unsigned char digest[outputBytes];
PKCS5_PBKDF2_HMAC(pass, strlen(pass), salt, strlen(salt), iterations, EVP_sha384(), outputBytes, digest);
for (i = 0; i < sizeof(digest); i++)
sprintf(hexResult + (i * 2), "%02x", 255 & digest[i]);
}
void PBKDF2_HMAC_SHA_512_string(const char* pass, const unsigned char* salt, int32_t iterations, uint32_t outputBytes, char* hexResult)
{
unsigned int i;
unsigned char digest[outputBytes];
PKCS5_PBKDF2_HMAC(pass, strlen(pass), salt, strlen(salt), iterations, EVP_sha512(), outputBytes, digest);
for (i = 0; i < sizeof(digest); i++)
sprintf(hexResult + (i * 2), "%02x", 255 & digest[i]);
}
Пример использования будет:
// 2*outputBytes+1 is 2 hex bytes per binary byte,
// and one character at the end for the string-terminating \0
char hexResult[2*outputBytes+1];
memset(hexResult,0,sizeof(hexResult));
PBKDF2_HMAC_SHA_1nat_string(pass, salt, iterations, outputBytes, hexResult);
printf("%s\n", hexResult);
или же
// 2*outputBytes+1 is 2 hex bytes per binary byte,
// and one character at the end for the string-terminating \0
char hexResult[2*outputBytes+1];
memset(hexResult,0,sizeof(hexResult));
PBKDF2_HMAC_SHA_512_string(pass, salt, iterations, outputBytes, hexResult);
printf("%s\n", hexResult);
Используйте случайную соль для каждого пользователя от 8 до 16 двоичных байтов, то есть от 16 до 32 шестнадцатеричных цифр - мой код пока не имеет примеров генерации этого
Независимо от того, что вы выберете, не забудьте проверить его по тестовым векторам (некоторые из них находятся в pbkdf2_test.bat/sh в моем репозитории).
Кроме того, в вашей системе проведите некоторое тестирование - конечно, в вариантах PBKDF2-HMAC-SHA-384 и PBKDF2-HMAC-SHA-512, компиляция в 64-битной системе дает значительно лучшие результаты. Сравните это с моими одинаково плохими C++ Crypto++ и / или моими плохими примерами C PolarSSL или примером реализации Jither C#, в зависимости от того, какая у вас целевая система.
Причина, по которой вы заботитесь о скорости, заключается в том, что вы должны выбрать количество итераций, основанное на производительности вашей производственной системы, по сравнению с количеством пользователей, которые входят в систему / создают пароли в пиковое время, чтобы не вызывать слишком много жалоб на медлительность.
Злоумышленники будут использовать что-то вроде oclHashcat, который на одном ПК с 8-кратным ядром AMD R9 290Xstock может пытаться угадать 3.4E12 (2^41) каждые 30 дней против PBKDF2-HMAC-SHA-1(SSID как соль, пароль Длина вывода 32 байта, 4096 итераций, или WPA/WPA2), что более или менее эквивалентно PBKDF2-HMAC-SHA-1 (соль, pw, длина вывода 20 байтов, 8192 итерации).
- если вы используете 65536 итераций, злоумышленник сможет проверять предположения 8.5E11 (~2^39) каждые 30 дней.
- если вы используете 1024 итерации, злоумышленник сможет вместо этого пробовать догадки 2.7E13 (~2^44) каждые 30 дней.
Разница становится важной, когда атакующий начинает выбирать свои атаки.
- В обоих случаях злоумышленник собирается грубо взломать очень маленькие ключи; нет причин не тратить несколько минут или даже несколько часов на низко висящие фрукты.
- Это все шестнадцатеричные символы для длины 1-n, а затем все печатные символы от длины n+1 до n+m, и затем, продолжающиеся вниз по линии, пока они не будут в n+y с жестко закодированными! в конце:).
- В обоих случаях злоумышленник собирается использовать крошечные словари и крошечные наборы правил; скажем, словарь phpbb из 184389 слов и набор правил Best64
- Если у вас было 1000 пользователей с 1000 различными случайными солями и вы использовали PBKDF2-HMAC-SHA-1 с 65536 итерациями, то для нашего единственного ПК потребуется 8 атакующих GPU (184389*64*1000)/(8.5E11/30) дней = 0,41 дней. Это того стоит!
- Теперь, что насчет того же словаря phpbb и среднего размера T0X1C набора правил из 4089 правил?
- Если у вас было 1000 пользователей с 1000 различными случайными солями и вы использовали PBKDF2-HMAC-SHA-1 с 65536 итерациями, то для нашего единственного ПК потребуется 8 атакующих GPU (184389*4089*1000)/(8.5E11/30) дней = 26,61 дней.
- Все еще стоит, но это почти 4 недели, чтобы эта машина потратила на одну атаку!
- Если у вас было 1000 пользователей с 1000 различными случайными солями и вы использовали PBKDF2-HMAC-SHA-1 с 65536 итерациями, то для нашего единственного ПК потребуется 8 атакующих GPU (184389*4089*1000)/(2.7E13/30) дней = 0,83 дня.
- Оно того стоит!
- Если у вас было 1000 пользователей с 1000 различными случайными солями и вы использовали PBKDF2-HMAC-SHA-1 с 65536 итерациями, то для нашего единственного ПК потребуется 8 атакующих GPU (184389*4089*1000)/(8.5E11/30) дней = 26,61 дней.
- А как насчет того же словаря phpbb и превосходного набора правил d3ead0ne из 35404 правил?
- Если у вас было 1000 пользователей с 1000 различными случайными солями и вы использовали PBKDF2-HMAC-SHA-1 с 65536 итерациями, то для нашего единственного ПК потребуется 8 атакующих GPU (184389*35404*1000)/(8.5E11/30) дней = 230,39 дней.
- Правильно, время покупать больше машин или работать над этим в нерабочее время.
- Если у вас было 1000 пользователей с 1000 различными случайными солями и вы использовали PBKDF2-HMAC-SHA-1 с 65536 итерациями, то для нашего единственного ПК потребуется 8 атакующих GPU (184389*4089*1000)/(2.7E13/30) дней = 7,18 дней.
- Стоило того! Это всего лишь неделя и еда.
- Если у вас было 1000 пользователей с 1000 различными случайными солями и вы использовали PBKDF2-HMAC-SHA-1 с 65536 итерациями, то для нашего единственного ПК потребуется 8 атакующих GPU (184389*35404*1000)/(8.5E11/30) дней = 230,39 дней.
Теперь для PBKDF2 есть еще несколько вещей, которые нужно знать:
- Для хэширования пароля никогда не выбирайте двоичный выходной размер больше, чем собственный размер хэша. Лично я бы не рекомендовал двоичный размер вывода менее 20 байт независимо, так что это немного ограничивает SHA-1.
- SHA-1 имеет размер 20 байт
- SHA-224 имеет размер 20 <= размер <= 28 байт
- SHA-256 имеет размер 20 <= размер <= 32 байта
- SHA-384 имеет размер 20 <= размер <= 48 байтов
- SHA-512 имеет размер 20 <= размер <= 64 байта
- отрегулируйте, как вы храните хеш, если он, конечно, не в двоичном виде.
- Причина: PBKDF2 сначала запускает количество итераций, запрошенных для одного собственного выходного размера (число справа, выше). Если вам нужно больше, он снова запускает весь счетчик итераций. Если вы хотели меньше, он усекается.
- Атакующий будет только соответствовать первому собственному размеру - если первые байты совпадают, да, это пароль, так что вам лучше увеличить количество итераций.