Цифровая подпись в криптографических системах представляет собой механизм, позволяющий подтвердить подлинность сообщения и его целостность без необходимости раскрытия секретного ключа. Основная идея заключается в том, что владелец закрытого ключа формирует подпись, а любой участник системы с доступом к открытому ключу может проверить её корректность.
В основе цифровых подписей лежит асимметричная криптография. Используется пара ключей:
Процесс подписи включает не само сообщение целиком, а его криптографический хеш. Это обеспечивает фиксированную длину входных данных и устойчивость к коллизиям при использовании современных хеш-функций.
H(m)
Хеш-функция преобразует сообщение m в фиксированное
значение H(m). Затем это значение используется в алгоритме
подписи.
Процесс можно представить в виде последовательности:
При проверке выполняется обратная операция:
Если они совпадают, подпись считается действительной.
В экосистеме JavaScript стандартом представления подписанных данных выступает JSON Web Signature (JWS). Он определяет структуру, в которой хранятся:
Формат позволяет передавать подписанные данные в компактном виде, часто в виде строки Base64URL.
Заголовок обычно содержит информацию об алгоритме:
{
"alg": "RS256",
"typ": "JWT"
}
Payload содержит данные, которые подписываются. Signature создаётся на основе header и payload.
В библиотеке jose поддерживается несколько криптографических алгоритмов:
Каждый алгоритм отличается характеристиками:
Библиотека jose предоставляет низкоуровневые и высокоуровневые API для работы с JWS.
Пример создания подписи:
import { SignJWT, generateKeyPair } from 'jose'
const { privateKey } = await generateKeyPair('RS256')
const jwt = await new SignJWT({ userId: 123 })
.setProtectedHeader({ alg: 'RS256' })
.setIssuedAt()
.setExpirationTime('2h')
.sign(privateKey)
В этом процессе:
Проверка выполняется с использованием открытого ключа:
import { jwtVerify } from 'jose'
const { payload } = await jwtVerify(token, publicKey)
console.log(payload)
Если подпись изменена или сообщение было модифицировано, проверка завершится ошибкой.
Компактное представление JWS состоит из трёх частей:
header.payload.signature
Каждая часть кодируется в Base64URL. Пример:
eyJhbGciOiJSUzI1NiJ9.eyJ1c2VySWQiOjEyM30.Kl3...abc
Изменение любой части приводит к недействительности подписи.
С точки зрения криптографии процесс можно формализовать:
= (H(m), private key)
и проверка:
(H(m), signature, public key)
Криптографическая подпись обеспечивает три ключевых свойства:
Эти свойства достигаются за счёт математической связи между закрытым и открытым ключами.
Генерация ключевой пары:
import { generateKeyPair } from 'jose'
const { publicKey, privateKey } = await generateKeyPair('ES256')
Экспорт и импорт ключей:
import { exportJWK, importJWK } from 'jose'
const jwk = await exportJWK(publicKey)
const key = await importJWK(jwk, 'ES256')
Использование формата JWK упрощает хранение и передачу ключей в распределённых системах.
На практике цифровые подписи часто нарушаются не из-за криптографии, а из-за неправильной реализации:
Особенно критична ошибка доверия полю alg, когда система
принимает произвольный алгоритм из заголовка токена.
Любое изменение данных приводит к изменению хеша, а следовательно — к изменению подписи. Даже минимальная модификация символа приводит к полностью другому значению:
H(m) H(m + )
Эта особенность делает цифровые подписи устойчивыми к подделке без доступа к закрытому ключу.
Хеш-функция выступает промежуточным слоем между данными и криптографическим алгоритмом. Её свойства:
Любое изменение входных данных приводит к полностью другому результату, что критично для проверки целостности.
JWT (JSON Web Token) использует JWS для защиты данных. Подпись гарантирует, что:
В связке с jose JWT формируется и проверяется через единый криптографический механизм, основанный на цифровых подписях.