Лучший способ использовать шифрование AES с помощью HMAC для шифрования / дешифрования исключительно на стороне клиента

#encryption #cryptography #cryptojs

#шифрование #криптография #cryptojs

Вопрос:

Я просто играю с тестовым приложением, чтобы зашифровать паролем некоторый текст, а затем расшифровать с помощью пароля. Я хочу, чтобы все было сделано на стороне клиента (в настоящее время используется CryptoJS).

Проблема, с которой я сталкиваюсь, заключается в том, что AES не имеет представления о том, правильно ли расшифрован текст или нет. Я мог бы расшифровать с неправильным паролем и не знать (очевидно, что текст будет нечитаемым для человека, но код этого не знает). Кодирование часто выдает ошибку, но не всегда.

Мое решение заключается в использовании HMAC следующим образом.

 encrypted = AES.encrypt(plaintext, password) // Example: "eNcRyPtEd"
hmac = HmacSHA256(encrypted, password)       // Example: "HMACabc"
return `${encrypted}.${hmac}`                // Example: "eNcRyPtEd.HMACabc"
 

Затем сервер получает эту единственную строку, которая состоит из зашифрованного текста плюс HMAC, разделенного символом . .

При дешифровании:

 # Get guess from user
password_guess = "user's input"

# Calc HMAC with the password_guess
[encrypted, hmac] = payload.split(".")
newHmac = HmacSHA256(encrypted, password_guess)

# Compare newHmac with the original HMAC
# decrypt or error based on HMAC equality
if newHmac == hmac
  AES.decrypt(encrypted, password_guess)
else
  print "wrong password"
 

Это позволяет достичь моей первоначальной цели — иметь возможность определять, правильный пароль или нет, без необходимости сохранять / отправлять этот пароль или когда-либо оставлять его на клиенте. Однако я не криптограф, это было собрано из полусвязанных ответов SO и Википедии. И это проект для изучения, поэтому я хотел бы знать:

  • Есть ли недостатки в этом подходе?
  • Есть ли лучший способ справиться с этим?
  • Могу ли я что-нибудь сделать, чтобы улучшить свой текущий алгоритм?

Комментарии:

1. Почему вы не используете AES-GCM? Один ключ для обработки всего, но вам все равно нужен хороший пароль и KDF.

Ответ №1:

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

Для более полного описания этого подхода, основанного на sjcl, см. RNCrytor-js . Ваш базовый подход хорош, хотя cryptojs немного непрозрачен в отношении того, как он обрабатывает пароли (он делает это достаточно хорошо; он просто не очень хорошо документирует это, и некоторые из его вариантов не были хорошо проанализированы криптосообществом, например, повторное использование IV в качестве соли PBKDF2).

Использование пароля непосредственно в качестве ключа HMAC также плохо анализируется. Это, вероятно, достаточно хорошо, но неаккуратно. Как правило, вы никогда не должны повторно использовать ключ для двух целей. (В данном случае это не так, потому что ключ AES растягивается, а ключ HMAC — нет, но он все равно неаккуратный).

Если вы хотите создать подобный формат, вы можете посмотреть спецификацию RNCryptor v4. Это никогда не было полностью реализовано (и есть несколько неинкорпорированных комментариев от Мартена Бодевеса, который в этом разбирается лучше меня), но это может быть полезной отправной точкой. Он включает в себя много справочных данных, которые могут быть полезны для построения такого рода системы. Криптоформаты очень тонкие, и их сложно правильно построить.

Комментарии:

1. На грани использования одного и того же пароля для AES и HMAC. Если я хочу запросить только один ввод от пользователя, могу ли я использовать этот ввод для получения двух других паролей (я бы поискал лучшее решение, но только для примера: хэшируйте его, затем разделите хэш пополам), один для AES и один для HMAC? Или это ничем не отличается от использования однопользовательского ввода для обоих?

2. И спасибо, я покопаюсь в RNCryptor. Я не сталкивался с этим раньше.

3. Инструмент, который вы ищете, называется HKDF. Сначала растяните пароль в IKM (начальный ключевой материал). Затем используйте HKDF для извлечения дополнительных ключей. Спецификация v4 включает объяснение процесса. Но да, более простым подходом (и довольно безопасным) было бы использовать PBKDF2 для растягивания пароля на 512 бит, а затем разделить его на 256 для AES и 256 для HMAC. Каждый выходной бит ключа с расширением PBKDF считается независимым, поэтому вы можете безопасно разделить биты и использовать их для независимых целей.

4. Текущая версия v3 RNCryptor дважды запускает PBKDF2 для пароля с разными солями для генерации двух независимых ключей. Это безопасно, но очень медленно, поэтому я предложил использовать HKDF для версии 4 вместо этого. Кроме того, если вы можете, вам следует просто использовать AES-GCM. Он решает все эти проблемы вместе и стал гораздо более доступным. При использовании GCM отдельный HMAC не требуется. (Тем не менее, вам все еще требуется PBKDF для расширения паролей, созданных человеком.)

5. Я бы больше беспокоился о том, что для вычисления ключа HMAC не используется PBKDF. Это делает схему значительно менее безопасной, поскольку злоумышленник может напрямую проверять пароли. Использование одного и того же ключа после PBKDF для HMAC и AES — ну, не доказано, что это безопасно, но я был бы очень удивлен, если кто-нибудь сможет организовать атаку на основе этого.