Hmac.new: примеры (PYTHON)

Использование функции hmac.new в Python для криптографической аутентификации
Раздел: Криптография, Хэширование
hmac.new(key: bytes, msg: bytes = None, digestmod: str = 'md5'): hmac.HMAC

Базовое описание функции hmac.new

Функция hmac.new() из стандартного модуля Python hmac используется для создания объектов HMAC (Hash-based Message Authentication Code). Она применяется для проверки целостности и подлинности сообщений. Механизм HMAC объединяет хеш-функцию с секретным ключом, что позволяет получать криптографическую хеш-сумму, подделать которую без знания ключа сложно.

Сигнатура функции:
hmac.new(key, msg=None, digestmod='')

Аргументы:

  • key (обязательный) - секретный ключ в виде байтового объекта (bytes или bytearray). Рекомендуется, чтобы его длина была не меньше размера выхода хеш-функции.
  • msg (необязательный) - исходное сообщение для аутентификации (также в байтах). Может быть None. Сообщение можно передать позже с помощью методов update().
  • digestmod (необязательный) - определяет используемую хеш-функцию. Может быть строкой с названием алгоритма (например, 'md5', 'sha256'), самим модулем хеширования (например, hashlib.sha256) или конструктором (например, hashlib.sha256). В версиях Python 3.4+ является обязательным аргументом.

Возвращаемое значение: объект hmac.HMAC, который имеет методы, аналогичные объектам модуля hashlib (update(), digest(), hexdigest()).

Простые примеры использования

Базовый пример с передачей ключа и сообщения сразу.

import hmac, hashlib

key = b'secret-key'
message = b'Important message'

h_obj = hmac.new(key, message, digestmod='sha256')
print("HMAC hex:", h_obj.hexdigest())
HMAC hex: 9c7c2a3f7e... (уникальная хеш-сумма)

Постепенная передача данных с помощью update().

import hmac, hashlib

h_obj2 = hmac.new(b'key', digestmod=hashlib.sha1)
h_obj2.update(b'part1')
h_obj2.update(b'part2')
print("HMAC digest:", h_obj2.digest())
print("HMAC hex:", h_obj2.hexdigest())
HMAC digest: b'\x87\x02...' (байтовое представление)
HMAC hex: 8702... (шестнадцатеричное представление)

Использование различных алгоритмов хеширования.

import hmac

key = b'test'
msg = b'data'

hmac_md5 = hmac.new(key, msg, digestmod='md5')
hmac_sha512 = hmac.new(key, msg, digestmod='sha512')

print("MD5:", hmac_md5.hexdigest())
print("SHA512:", hmac_sha512.hexdigest())
MD5: 0b6e... (хеш MD5)
SHA512: e3a5... (хеш SHA-512)

Похожие функции в Python

В модуле hashlib существуют прямые хеш-функции (hashlib.md5(), hashlib.sha256() и др.). Они вычисляют обычную криптографическую хеш-сумму, но без использования ключа. Эти функции подходят для проверки целостности данных, но не для аутентификации источника.

Функция hmac.compare_digest(a, b) из того же модуля применяется для безопасного сравнения двух хеш-сумм или строк, чтобы противостоять атакам по времени. Ее рекомендуют использовать вместо оператора ==.

Выбор между hmac.new() и hashlib зависит от задачи: если требуется подтвердить, что данные созданы владельцем секретного ключа, используется HMAC. Для простой проверки целостности без требований к аутентификации источника может быть достаточно hashlib.

Реализации HMAC в других языках программирования

JavaScript (Node.js): Используется модуль crypto.

const crypto = require('crypto');
const hmac = crypto.createHmac('sha256', 'secret-key');
hmac.update('message');
console.log(hmac.digest('hex'));
(выведет шестнадцатеричную строку)

Java: Класс javax.crypto.Mac.

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec("secret-key".getBytes(), "HmacSHA256");
mac.init(keySpec);
byte[] result = mac.doFinal("message".getBytes());
// преобразование result в hex
(массив байтов result)

PHP: Функция hash_hmac().

$hmac = hash_hmac('sha256', 'message', 'secret-key', true); // true для сырых данных
echo bin2hex($hmac);
(шестнадцатеричное представление)

Golang: Пакет crypto/hmac.

import "crypto/hmac"
import "crypto/sha256"

key := []byte("secret-key")
msg := []byte("message")
h := hmac.New(sha256.New, key)
h.Write(msg)
result := h.Sum(nil)
// fmt.Printf("%x", result)
(срез байтов result)

Основное отличие от Python заключается в синтаксисе и способе указания алгоритма, но концепция идентична: ключ, сообщение и функция хеширования.

Типичные ошибки при работе с функцией

1. Передача строки вместо байтов.

import hmac
# Неправильно:
try:
    h = hmac.new('mykey', 'mymsg', digestmod='sha256')
except TypeError as e:
    print("Ошибка:", e)
Ошибка: key: expected bytes or bytearray, but got 'str'

2. Отсутствующий или неверный аргумент digestmod в новых версиях Python.

import hmac
# В Python 3.4+ это вызовет ошибку:
try:
    h = hmac.new(b'key', b'msg')
except TypeError as e:
    print("Ошибка:", e)
Ошибка: digestmod должен быть указан

3. Использование короткого или пустого ключа. Хотя функция отработает, это снижает криптостойкость.

import hmac
h = hmac.new(b'', b'msg', digestmod='sha256') # Пустой ключ
print("HMAC с пустым ключом:", h.hexdigest())
HMAC с пустым ключом: 6f... (вычисляется, но не безопасно)

4. Попытка использовать hexdigest() или update() после вызова digest(). Объект становится "завершенным".

import hmac
h = hmac.new(b'k', b'm', digestmod='sha256')
d = h.digest()
try:
    h.update(b'more') # Попытка обновить после получения дайджеста
except AttributeError as e:
    print("Ошибка:", e)
Ошибка: объект уже завершен

Изменения в функции в последних версиях Python

Наиболее значимое изменение произошло в Python 3.4. Аргумент digestmod перестал быть необязательным. Если его не указать, возникает исключение TypeError. Это изменение подчеркивает важность явного выбора хеш-алгоритма для безопасности.

В Python 3.8 добавлена поддержка передачи конструктора хеш-функции из модуля hashlib в качестве аргумента digestmod наряду с использованием строки. Это повышает гибкость и ясность кода.

Пример для Python 3.8+:

import hmac, hashlib
# Оба варианта корректны:
h1 = hmac.new(b'key', digestmod='sha3_256')
h2 = hmac.new(b'key', digestmod=hashlib.sha3_256)

Расширенные примеры использования HMAC

1. Проверка подлинности Webhook запроса. Часто используется для верификации запросов от сторонних сервисов.

Пример python
import hmac, hashlib

# Сервер генерирует подпись
secret = b'webhook-secret'
payload = b'{"event":"update","id":123}'
computed_signature = hmac.new(secret, payload, hashlib.sha256).hexdigest()
print("Серверная подпись:", computed_signature)

# Клиент проверяет подпись (имитация)
received_signature = computed_signature # Предположим, получена из заголовка
is_valid = hmac.compare_digest(received_signature, computed_signature)
print("Подпись верна?", is_valid)
Серверная подпись: 1a2b3c...
Подпись верна? True

2. Создание временных токенов с истекшим сроком действия (Timed HMAC). В подпись добавляется метка времени.

Пример python
import hmac, hashlib, time, json

def create_token(secret, data, validity_sec=30):
    expiry = int(time.time()) + validity_sec
    data['exp'] = expiry
    message = json.dumps(data, sort_keys=True).encode()
    token_hmac = hmac.new(secret, message, hashlib.sha256).hexdigest()
    return f"{expiry}.{token_hmac}"

def verify_token(secret, token):
    try:
        expiry_str, received_hmac = token.split('.')
        expiry = int(expiry_str)
        if expiry < time.time():
            return False, "Token expired"
        # Пересчитываем HMAC для проверки (данные - только expiry)
        calc_hmac = hmac.new(secret, json.dumps({'exp':expiry}, sort_keys=True).encode(), hashlib.sha256).hexdigest()
        if not hmac.compare_digest(calc_hmac, received_hmac):
            return False, "Invalid signature"
        return True, "Valid"
    except Exception:
        return False, "Malformed token"

secret = b'super-secret'
token = create_token(secret, {'user_id': 42})
print("Токен:", token)
print("Проверка:", verify_token(secret, token))
Токен: 1640995200.abc123...
Проверка: (True, 'Valid') или (False, 'Token expired')

3. Построение ключевой производной функции (KDF) на основе HMAC (HKDF). Упрощенная демонстрация идеи.

Пример python
import hmac, hashlib

def hkdf_extract(salt, ikm, hash_algo=hashlib.sha256):
    """Фаза извлечения"""
    if salt is None:
        salt = bytes([0] * hash_algo().digest_size)
    return hmac.new(salt, ikm, hash_algo).digest()

def hkdf_expand(prk, info, length, hash_algo=hashlib.sha256):
    """Фаза расширения"""
    hash_len = hash_algo().digest_size
    n = (length + hash_len - 1) // hash_len
    t = b''
    okm = b''
    for i in range(1, n+1):
        t = hmac.new(prk, t + info + bytes([i]), hash_algo).digest()
        okm += t
    return okm[:length]

# Пример использования:
salt = b'random-salt'
input_key_material = b'master-key'
info = b'application context'

prk = hkdf_extract(salt, input_key_material)
derived_key = hkdf_expand(prk, info, 32) # Получаем 32 байта
print("Производный ключ (hex):", derived_key.hex())
Производный ключ (hex): a1b2c3... (32 байта)

питон hmac.new function comments

En
Hmac.new Return a new hmac object