Надежность ключа AES на основе PBKDF

Вот заглушка системы, которая будет генерировать пару ключей, используя AES 256 CBC в OpenSSL. Цель приведенного ниже кода — сгенерировать два случайных пароля, ключ AES и некоторые другие общедоступные данные. Ключ AES будет использоваться для обмена общими секретами.

Отказ от ответственности: я НЕ являюсь экспертом в области криптографии или систем безопасности. Я понимаю опасности, но смысл этого упражнения в академическом интересе. Если есть ошибки новичка или что-то совершенно и опасно неправильное, пожалуйста, укажите на это, чтобы помочь мне в обучении.

// The key_generator() will produce the following public keys in addition
// to a couple of other private keys.
// public_identifier
// public_salt
// public_composite_identifier
// public_aes_key

int key_generator(/*some args*/)
{
    // Step 1
    //Obtain public_identifier. Possibly a hashed value of an unique ASCII string.
    unsigned char *public_identifier;


    // Step 2
    //Generate 256 bit private_primary_random_passkey which is secret. 
    //This random key is generated once and reused later.
    unsigned char *private_primary_random_passkey;

    if(RAND_bytes(private_primary_random_passkey, 256) == 0)     
        return FAILURE;


    // Step 3
    //Generate private_composite_identifier using public_identifier
    //and private_primary_random_passkey.
    //IMPORTANT - The method to obtain private_composite_identifier 
    //may be publicly known.  
    //The public_identifier is also publicly known but the 
    //private_primary_random_passkey is secret.
    unsigned char *private_composite_identifier;

    //<Some code for generating private_composite_identifier>
    //.....
    //</code>


    // Step 4     
    //Generate temporary temp_private_aes_key and temp_private_aes_IV;
    //NOTE - Used dummy vars wherever key length is required. 
    //Assume correct length is passed in.

    int aes_rounds = 25000;

    unsigned char *temp_private_aes_key;
    unsigned char *temp_private_aes_IV;

    if(EVP_BytesToKey(EVP_aes_256_cbc(), 
                      EVP_sha512(), 
                      private_composite_identifier, 
                      private_primary_random_passkey, 
                      private_composite_identifier_length/8, 
                      aes_rounds, 
                      temp_private_aes_key, 
                      temp_private_aes_IV) == 0)     
        return FAILURE;    


    // Step 5
    //Generate 128 bit random salt which is public.
    unsigned char *public_salt;

    if(RAND_bytes(public_salt, 128) == 0)     
        return FAILURE;


    // Step 6
    //Generate private_composite_identifier and public_composite_identifier 
    //using temp_private_aes_key and public_salt.
    unsigned char *public_composite_identifier;
    unsigned char *private_composite_identifier;

    if(EVP_BytesToKey(EVP_aes_256_cbc(), 
                      EVP_sha512(), 
                      temp_private_aes_key,
                      public_salt,
                      temp_private_aes_key_length/8, 
                      aes_rounds, 
                      private_composite_identifier, 
                      public_composite_identifier) == 0)     
        return FAILURE;    


    // Step 7
    //Generate 128 bit private_secondary_random_passkey which is secret. 
    //This random key is generated once and reused later.
    unsigned char *private_secondary_random_passkey;

    if(RAND_bytes(private_secondary_random_passkey, 128) == 0)     
        return FAILURE;

    unsigned char *private_aes_key;
    unsigned char *public_aes_key;

    if(EVP_BytesToKey(EVP_aes_256_cbc(), 
                      EVP_sha512(), 
                      private_composite_identifier,
                      private_secondary_random_passkey,
                      private_composite_identifier_length/8, 
                      aes_rounds, 
                      private_aes_key, 
                     public_aes_key) == 0)     
        return FAILURE
}

Вот мои вопросы:

  • Следует ли использовать пару ключей RSA вместо ключа AES? Почему одно предпочтительнее другого?
  • Поскольку ключи, используемые для создания пары ключей, длинные, генерируются случайным образом и содержат соли, безопасно ли использовать ту же пару ключей AES/RSA позже? Я понимаю риски, связанные с таблицами Rainbow и другими мерами, но разве эти проблемы не облегчаются с помощью случайных солей и ключей, за которыми следуют три уровня генерации ключей?
  • Какими способами злоумышленник может воссоздать пару ключей или взломать эту систему, используя общедоступные данные?
  • Любые другие моменты, которые вы можете придумать, чтобы помешать или улучшить эту систему.

Спасибо за ваше время.


person Nilesh Karia    schedule 10.01.2014    source источник
comment
АЭС симметричен. РСА асимметричный.   -  person Jonathon Reinhart    schedule 10.01.2014
comment
@JonathonReinhart - Спасибо, что указали на ошибку n00b. :) Не могли бы вы пересмотреть вопросы, если на шаге 7 мы сгенерируем пару ключей RSA из доступной до этого момента информации и используем эту пару ключей для будущего шифрования?   -  person Nilesh Karia    schedule 10.01.2014


Ответы (1)


Вы действительно занимаетесь криптографической инженерией, и люди на Crypto Stack Exchange, вероятно, лучше подходят для таких вопросов проектирования.

Кроме того, вам не хватает подробностей для каких-либо полезных ответов. (Вернитесь, когда вопрос будет более ориентированным на реализацию и конкретным).


Надежность ключа AES на основе PBKDF

Во-первых, название.

Эта проблема так же сложна, как угадать пароль, и, вероятно, проще с такими вещами, как многомиллионные списки слов из прошлых утечек данных. То есть ваш злоумышленник не будет пытаться взломать ключ AES — он или она попытается угадать пароль, который вы использовали для получения ключа.

Чтобы ввести ключ AES-128 со 128-битной защитой, вам потребуется 128-битная энтропия в пароле. Пароли не случайны, и английский имеет около 1,3 бита энтропии на букву, поэтому ожидайте использования пароля длиной более 14 символов для AES-128. Используйте пароль из 28+ символов, если вы используете AES-256.


Цель приведенного ниже кода — сгенерировать два случайных пароля, ключ AES и некоторые другие общедоступные данные. Ключ AES будет использоваться для обмена общими секретами.

Какова цель здесь? Создать безопасный канал? Или просто обменяться ключами?

Обычно вы используете открытый ключ для передачи или соглашаетесь на передачу симметричных или сеансовых ключей. То есть: (1) использовать метод Диффи-Хеллмана для создания общего секрета («соглашение о ключах», поскольку обе стороны добавляют к процессу), а затем передавать симметричные сеансовые ключи под общим секретом; или (2) использовать RSA для передачи «главного» симметричного сеансового ключа («передача ключа», поскольку одна сторона генерирует секрет, шифрует его с использованием ключа RSA партнера, а затем отправляет его партнеру).

Существуют обмены ключами с проверкой подлинности на основе общих секретов и паролей. SSL/TLS имеет два из них — Preshared Key (PSK) и Secure Remote Password (SRP). PSK использует AES в качестве примитива, а SRP использует Diffie-Hellman.

PSK и SRP обычно лучше, чем DH и RSA, потому что они обеспечивают (1) взаимную аутентификацию и (2) привязку канала. Судя по вашему утверждению, что «ключ AES будет использоваться для обмена общими секретами», PSK может быть идеальным для вас. Но не очевидно, являются ли они хорошим выбором, потому что мы не знаем, чего вы пытаетесь достичь.


Следует ли использовать пару ключей RSA вместо ключа AES? Почему одно предпочтительнее другого?

Это зависит от вашей проблемной области и того, что вы пытаетесь сделать.

Если на сервере есть открытый ключ RSA, вы должны использовать Диффи-Хеллмана или RSA. Если вы и сервер используете общий секрет, избегайте использования открытого ключа и используйте PSK или SRP.

Методы Диффи-Хеллмана и RSA требуют больших вычислительных ресурсов. Обычно они используются для обмена или транспортировки ключей симметричного шифрования. RSA-2048 может выполнять 4 или 5 миллионов инструкций и снижает производительность.

Симметричные шифры быстры с точки зрения вычислений. Они используются для большей части трафика после завершения Diffie-Hellman или RSA. Мы называем это массовым шифрованием, и они жирные. Если у вас есть аппаратное обеспечение Ivy Bridge, то AES-NI может приблизиться к 1 циклу/байту для зашифрованных данных.

Когда вы читаете, что веб-серверы, такие как nginx, могут обрабатывать только 1000 соединений SSL/TLS, вы видите эффекты Диффи-Хеллмана и RSA. Как только эти каналы будут настроены и настроены для массового шифрования, он сможет обрабатывать тысячи потоков зашифрованного трафика.

Безопасно ли использовать ту же пару ключей AES/RSA позже?

Это яблоки и апельсины. Обычно вы используете пару ключей RSA на длительный срок, а ключи AES ограничены сеансом.

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


Я понимаю риски, связанные с таблицами Rainbow и другими мерами, но разве эти проблемы не облегчаются с помощью случайных солей и ключей, за которыми следуют три уровня генерации ключей?

Это зависит от вашей проблемной области и того, что вы пытаетесь сделать. Мы не можем на самом деле ответить, потому что мы не знаем, чего вы пытаетесь достичь.

См. памятку по хранению паролей Джона Стивена на OWASP. Он проведет вас через модели угроз и объяснит части системы. Подробное описание доступно в разделе Безопасное хранилище паролей.


Какими способами злоумышленник может воссоздать пару ключей или взломать эту систему, используя общедоступные данные?

Это зависит от вашей проблемной области и того, что вы пытаетесь сделать. Мы не можем на самом деле ответить, потому что мы не знаем, чего вы пытаетесь достичь.

Управление ключами — самая сложная часть криптографии, и все, что мы видим, — это вакуум:

int key_generator(/*some args*/)

Вам нужно будет подробно описать все параметры, как вы ими управляете и как вы их используете.


Любые другие моменты, которые вы можете придумать, чтобы помешать или улучшить эту систему.

Это выскакивает: три вызова RAND_byes и EVP_BytesToKey. Как правило, вы делаете это:

byte[] master = Random(16) // or 32, or however many you need
byte[] key1 = Derive(master, "<some usage>", <other distinguishing information>)
byte[] key2 = Derive(master, "<some usage>", <other distinguishing information>)
byte[] key3 = Derive(master, "<some usage>", <other distinguishing information>)

master может исходить от обмена ключами или соглашения о ключах.

key1, key2 и key3 будут отличаться, потому что (1) использование отличается для разных вызовов и (2) дополнительная отличительная информация отличается для одноранговых узлов.

Derive может быть HMAC или CMAC и включает в себя элементы KDF. Также посмотрите на HKDF, который является современным для алгоритмов «расширить, а затем извлечь».

person jww    schedule 11.01.2014
comment
Спасибо за очень подробный ответ! Во-первых, цель: создать систему, в которой клиенты могут детерминистически (повторно) генерировать пары открытых ключей, которые будут использоваться для обмена данными с использованием существующих пар ключей. Пароли не обязательно должны быть на английском языке. Шестнадцатеричные ключи будут повторно введены для повторного создания пар ключей, когда клиенты захотят обменяться данными. Вот почему пароли генерируются случайным образом в заглушке и представляются регистрирующемуся клиенту. После первоначальной генерации ключей каждый клиент будет иметь некоторый уникальный идентификатор, связанный с открытым ключом и общедоступной солью. - person Nilesh Karia; 14.01.2014
comment
Я понимаю вашу точку зрения об использовании производных методов, а не отдельных вызовов EVP_BytesToKey (вызов RAND_bytes предназначен только для первоначальной регистрации клиента). Я буду работать над уточненной тестовой реализацией с учетом изложенных вами моментов и вернусь. При этом мне не удалось найти хороший пример создания пары ключей RSA (или любой другой пары асимметричных ключей) детерминированным образом из заданного начального числа (в данном случае 256-битного случайного начального числа) в OpenSSL для C/C++. Было бы очень полезно, если бы вы или кто-то еще указал мне на такой пример. - person Nilesh Karia; 14.01.2014