Верификация подписи

В библиотеке Stanford JavaScript Crypto Library работа с цифровыми подписями основана на асимметричных алгоритмах, чаще всего ECDSA (Elliptic Curve Digital Signature Algorithm). Подпись используется для подтверждения того, что сообщение действительно создано владельцем приватного ключа и не было изменено после подписания.

Проверка подписи (signature verification) — это операция, которая позволяет убедиться в двух ключевых свойствах:

  • целостность данных (данные не изменены)
  • подлинность отправителя (подпись создана соответствующим приватным ключом)

Основные компоненты системы подписей в SJCL

Для работы с подписями в SJCL используются следующие элементы:

  • приватный ключ (private key) — используется для создания подписи
  • публичный ключ (public key) — используется для проверки подписи
  • сообщение (message) — данные, которые подписываются
  • подпись (signature) — криптографический результат хэширования и шифрования

В SJCL ключи обычно представлены в формате объектов или сериализованных JSON-структур.


Алгоритм ECDSA в контексте SJCL

ECDSA основан на математике эллиптических кривых. Его ключевая идея заключается в том, что:

  • приватный ключ генерирует подпись
  • публичный ключ проверяет подпись
  • вычислительно невозможно восстановить приватный ключ по публичному

Подпись состоит из двух чисел:

  • r
  • s

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


Подготовка ключей

Перед проверкой подписи необходимо иметь корректно сформированные ключи.

Пример генерации пары ключей:

var keypair = sjcl.ecc.elGamal.generateKeys(256);

var pub = keypair.pub;
var priv = keypair.sec;

Публичный ключ используется на стороне проверки, приватный — на стороне подписи.


Создание подписи

Подпись создаётся приватным ключом:

var message = "hello world";

var hash = sjcl.hash.sha256.hash(message);

var signature = priv.sign(hash);

Важно понимать, что SJCL не подписывает строку напрямую — сначала вычисляется хэш сообщения.


Сериализация подписи

Подпись обычно хранится в сериализованном виде:

var sigJson = sjcl.codec.json.fromBits(signature);

Это позволяет передавать подпись по сети или сохранять в базе данных.


Проверка подписи

Процесс проверки выполняется с использованием публичного ключа.

Базовый алгоритм проверки

  1. Получить сообщение
  2. Вычислить его хэш
  3. Получить подпись
  4. Использовать публичный ключ для верификации

Пример кода проверки

var message = "hello world";

var hash = sjcl.hash.sha256.hash(message);

var sig = sjcl.codec.json.toBits(sigJson);

var valid = pub.verify(hash, sig);

Если подпись корректна, переменная valid будет равна true.


Важные аспекты безопасности

Уникальность nonce

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

SJCL автоматически генерирует nonce, но при кастомных реализациях важно избегать ручного вмешательства.


Хэширование перед подписью

SJCL не выполняет скрытое хэширование внутри функции подписи. Это означает:

  • разработчик обязан явно хэшировать данные
  • одинаковые сообщения всегда дают одинаковый хэш
  • подпись зависит от хэша, а не от исходной строки

Формат данных при проверке

При работе с SJCL важно соблюдать совместимость форматов:

  • сообщение → массив бит (bitArray)
  • подпись → bitArray
  • ключи → ECC-объекты SJCL

Любое несоответствие форматов приводит к ложному отрицанию подписи.


Типичные ошибки при верификации

1. Разный алгоритм хэширования

Если подпись создавалась с SHA-256, а проверка выполняется с другим алгоритмом — результат всегда будет false.


2. Изменение исходного сообщения

Даже изменение одного символа полностью меняет хэш:

  • “hello” ≠ “hello”

3. Неправильная сериализация подписи

Частая ошибка:

  • подпись сохраняется как JSON
  • при восстановлении теряется структура bitArray

Проверка подписи в реальных сценариях

Проверка сообщений от сервера

Типичный сценарий:

  • сервер отправляет сообщение + подпись
  • клиент проверяет подпись публичным ключом сервера
var data = response.data;
var signature = response.signature;

var hash = sjcl.hash.sha256.hash(data);

var sigBits = sjcl.codec.base64.toBits(signature);

var isValid = serverPubKey.verify(hash, sigBits);

Проверка подписанных токенов

В системах авторизации подписи используются для JWT-подобных токенов:

  • payload подписывается приватным ключом
  • клиент проверяет подлинность без обращения к серверу

Внутренний механизм verify()

Метод:

pub.verify(hash, signature)

выполняет следующие шаги:

  1. декодирует подпись (r, s)
  2. вычисляет вспомогательные значения эллиптической кривой
  3. проверяет равенство математического выражения ECDSA
  4. возвращает boolean результат

Производительность проверки

Проверка подписи в SJCL:

  • быстрее, чем генерация подписи
  • зависит от размера ключа (256, 384, 521 бит)
  • может выполняться десятки тысяч раз в секунду в браузере

Использование в браузере

SJCL полностью совместима с браузерной средой:

if (pub.verify(hash, sig)) {
    console.log("подпись корректна");
} else {
    console.log("подпись недействительна");
}

Проверка выполняется синхронно и не требует серверной логики.


Взаимодействие с другими криптосистемами

Подписи SJCL не всегда совместимы с внешними библиотеками без преобразования:

  • OpenSSL использует DER-формат
  • SJCL использует bitArray
  • требуется ручная конвертация

Практическая модель доверия

Проверка подписи в SJCL строится на принципе:

  • доверие к публичному ключу
  • недоверие к сети
  • невозможность подделки без приватного ключа

Это делает систему устойчивой к MITM-атакам при корректной реализации ключевого обмена.


Поведение при ошибке проверки

Метод verify() всегда возвращает:

  • true — подпись совпадает
  • false — подпись невалидна

Исключения не выбрасываются, что упрощает обработку в продуктивных системах.


Работа с несколькими подписями

SJCL позволяет проверять несколько подписей для одного сообщения:

  • мультиподписи реализуются на уровне приложения
  • каждая подпись проверяется отдельно
  • итоговое решение принимается логикой системы