Как реализовать протокол Диффи-Хеллмана (Diffie-Hellman) в Python?

«Как реализовать протокол Диффи-Хеллмана (Diffie-Hellman) в Python?» — вопрос из категории Безопасность, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Протокол Диффи-Хеллмана (Diffie-Hellman, DH) — это криптографический протокол для безопасного обмена ключами по незащищенному каналу. Он позволяет двум сторонам сгенерировать общий секретный ключ, который затем может быть использован для симметричного шифрования.

В Python реализация DH обычно осуществляется с помощью криптографических библиотек, таких как cryptography или PyCryptodome.

Принцип работы:

  1. Общие параметры: Стороны (например, Алиса и Боб) договариваются об общих публичных параметрах: большом простом числе p (модуль) и генераторе g.
  2. Генерация приватных ключей: Каждая сторона генерирует свой секретный (приватный) ключ a (для Алисы) и b (для Боба).
  3. Генерация публичных ключей: Каждая сторона вычисляет свой публичный ключ: A = g^a mod p (для Алисы) и B = g^b mod p (для Боба).
  4. Обмен публичными ключами: Алиса и Боб обмениваются своими публичными ключами.
  5. Вычисление общего секрета: Каждая сторона вычисляет общий секретный ключ: Алиса вычисляет S = B^a mod p, а Боб вычисляет S = A^b mod p. Математически B^a mod p = (g^b)^a mod p = g^(ba) mod p и A^b mod p = (g^a)^b mod p = g^(ab) mod p, что означает, что их секретные ключи будут одинаковыми.

Пример реализации с cryptography:

from cryptography.hazmat.primitives.asymmetric import dh
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend

# 1. Генерация параметров DH (обычно это делается один раз и параметры переиспользуются)
# generator=2 или 5, key_size - размер ключа в битах (2048, 3072, 4096)
parameters = dh.generate_parameters(generator=2, key_size=2048, backend=default_backend())

# 2. Генерация приватных ключей для Алисы и Боба
alice_private_key = parameters.generate_private_key()
bob_private_key = parameters.generate_private_key()

# 3. Получение публичных ключей
alice_public_key = alice_private_key.public_key()
bob_public_key = bob_private_key.public_key()

# 4. Обмен публичными ключами (в реальной системе это происходит по сети)
# Алиса получает публичный ключ Боба, Боб получает публичный ключ Алисы

# 5. Вычисление общего секретного ключа
alice_shared_key = alice_private_key.exchange(bob_public_key)
bob_shared_key = bob_private_key.exchange(alice_public_key)

# Проверка, что ключи совпадают
assert alice_shared_key == bob_shared_key

print("Общий секретный ключ успешно сгенерирован.")
# print(f"Ключ Алисы: {alice_shared_key.hex()}")
# print(f"Ключ Боба: {bob_shared_key.hex()}")

# Для использования в симметричном шифровании, общий ключ обычно хешируется
# from cryptography.hazmat.primitives import hashes
# from cryptography.hazmat.primitives.kdf.hkdf import HKDF
# 
# derived_key = HKDF( 
#     algorithm=hashes.SHA256(), 
#     length=32, 
#     salt=None, 
#     info=b'handshake data', 
#     backend=default_backend() 
# ).derive(alice_shared_key)
# print(f"Производный ключ: {derived_key.hex()}")

Ключевые аспекты и безопасность:

  • Размер ключа: Для обеспечения безопасности требуются достаточно большие простые числа (например, 2048 бит и более).
  • Уязвимость к MITM: Протокол Диффи-Хеллмана сам по себе уязвим к атакам "человек посередине" (Man-in-the-Middle, MITM), поскольку не предоставляет аутентификации сторон. Для защиты от MITM DH часто комбинируется с цифровыми подписями или сертификатами (как в TLS/SSL).
  • Эллиптические кривые (ECDH): В современных системах часто используется вариант на эллиптических кривых (ECDH), который обеспечивает аналогичный уровень безопасности при меньших размерах ключей, что приводит к более высокой производительности.