Подпись произвольного payload

Криптографическая подпись произвольных данных

Подпись данных в Jsrsasign строится вокруг идеи фиксации целостности и подлинности произвольного сообщения. Подписываемый payload не обязан быть структурированным в формате JWT — это может быть строка, JSON, бинарные данные в base64-представлении или результат сериализации бизнес-объекта.

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


Подготовка payload перед подписанием

Перед применением криптографических алгоритмов выполняется нормализация данных:

  • сериализация объектов в JSON
  • удаление лишних пробелов
  • фиксированный порядок ключей (канонизация JSON)
  • кодировка UTF-8

Типичная проблема подписи произвольного payload — различие строк при одинаковом семантическом содержимом:

const payload1 = JSON.stringify({ a: 1, b: 2 });
const payload2 = JSON.stringify({ b: 2, a: 1 });

Несмотря на идентичность данных, строки различаются, а значит подписи будут разными.

Для стабилизации используется ручная канонизация:

function canonicalize(obj) {
  return JSON.stringify(obj, Object.keys(obj).sort());
}

Базовая модель подписи в Jsrsasign

Jsrsasign предоставляет два уровня API:

  • высокоуровневый (KJUR.jws.JWS)
  • низкоуровневый (KJUR.crypto.Signature)

Для произвольных payload чаще применяется низкоуровневый API.


Подпись HMAC (симметричный ключ)

HMAC используется, когда один и тот же секрет применяется для подписи и проверки.

Пример HMAC-SHA256

const msg = "data-to-sign";
const secret = "shared-secret";

const hmac = new KJUR.crypto.Mac({ alg: "HmacSHA256", pass: secret });
hmac.updateString(msg);
const signature = hmac.doFinal();

Результат signature представлен в hex-формате. Для передачи по сети часто используется Base64:

const b64 = hextob64(signature);

RSA подпись произвольного payload

RSA требует приватного ключа в формате PEM.

Генерация подписи

const msg = "important-message";

const sig = new KJUR.crypto.Signature({ alg: "SHA256withRSA" });
sig.init(privateKeyPem);
sig.updateString(msg);
const signatureHex = sig.sign();

Преобразование в Base64:

const signatureB64 = hextob64(signatureHex);

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

const sig = new KJUR.crypto.Signature({ alg: "SHA256withRSA" });
sig.init(publicKeyPem);
sig.updateString(msg);

const isValid = sig.verify(signatureHex);

ECDSA подпись (кривые эллиптической криптографии)

ECDSA часто используется для компактных ключей и высокой производительности.

Пример secp256r1

const sig = new KJUR.crypto.Signature({ alg: "SHA256withECDSA" });
sig.init(privateKeyPem);
sig.updateString(msg);
const signature = sig.sign();

Проверка:

const verifier = new KJUR.crypto.Signature({ alg: "SHA256withECDSA" });
verifier.init(publicKeyPem);
verifier.updateString(msg);

const valid = verifier.verify(signature);

Работа с байтовым представлением

Jsrsasign по умолчанию работает со строками, но многие сценарии требуют явного управления байтами.

Проблема возникает при подписании:

  • бинарных протоколов
  • сериализованных структур
  • заранее хешированных данных

Используется updateHex:

const msgHex = "616263"; // "abc" в hex

const sig = new KJUR.crypto.Signature({ alg: "SHA256withRSA" });
sig.init(privateKeyPem);
sig.updateHex(msgHex);
const signature = sig.sign();

Подпись предхешированных данных

Иногда требуется подписывать уже вычисленный digest.

const md = new KJUR.crypto.MessageDigest({ alg: "sha256", prov: "cryptojs" });
md.updateString("payload");
const hashHex = md.digest();

Далее возможна подпись через RAW режим:

const sig = new KJUR.crypto.Signature({ alg: "NONEwithRSA" });
sig.init(privateKeyPem);
sig.updateHex(hashHex);
const signature = sig.sign();

Формирование устойчивого payload

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

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

Пример нормализации:

function normalizePayload(obj) {
  return JSON.stringify(obj, Object.keys(obj).sort(), 0);
}

Подпись произвольного JSON payload

const payload = {
  userId: 123,
  role: "admin",
  timestamp: 1710000000
};

const data = JSON.stringify(payload, Object.keys(payload).sort());

const sig = new KJUR.crypto.Signature({ alg: "SHA256withRSA" });
sig.init(privateKeyPem);
sig.updateString(data);

const signature = sig.sign();

Верификация JSON payload

const verifier = new KJUR.crypto.Signature({ alg: "SHA256withRSA" });
verifier.init(publicKeyPem);
verifier.updateString(data);

const valid = verifier.verify(signature);

Кодировка Base64URL для передачи подписи

При интеграции с веб-протоколами часто используется Base64URL:

function toBase64Url(hex) {
  return hextob64(hex)
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}

Частые ошибки при подписании payload

Несовпадение строкового представления

Разные сериализации JSON приводят к разным подписям даже при одинаковых данных.

Неверная кодировка

UTF-16 vs UTF-8 меняет байтовое представление строки.

Потеря каноничности

Объекты без фиксированного порядка ключей не пригодны для прямой подписи.

Подпись “сырого” объекта

Передача объекта без сериализации приводит к [object Object] в строковом виде.


Низкоуровневый API Signature как основной инструмент

KJUR.crypto.Signature предоставляет универсальный механизм:

  • выбор алгоритма
  • работа со строками и hex
  • поддержка RSA/ECDSA/DSA
  • контроль над этапом хеширования

Базовая структура использования:

const sig = new KJUR.crypto.Signature({ alg: "alg-name" });
sig.init(key);
sig.updateString(data);
const result = sig.sign();

Особенности подписания произвольных структур

Подпись произвольного payload в Jsrsasign фактически сводится к трём этапам:

  • приведение данных к детерминированной строке
  • выбор криптографического алгоритма
  • вычисление подписи над байтовым представлением

Эта модель универсальна для:

  • API аутентификации запросов
  • цифровых контрактов
  • подписания сообщений в распределённых системах
  • защиты вебхуков и событийных систем