Формирование запроса к TSA-серверу

Сервер службы доверенного времени (TSA, Time-Stamp Authority) принимает запросы, соответствующие стандарту RFC 3161. Основной объект запроса — структура TimeStampReq, формируемая в ASN.1 DER-формате и передаваемая обычно по HTTP POST.

Запрос всегда строится вокруг контрольной суммы исходных данных, а не самих данных. Это ключевой принцип: TSA не получает файл, он получает его хеш.

MessageImprint как основа запроса

Центральный элемент запроса — MessageImprint, который включает:

  • идентификатор алгоритма хеширования
  • сам хеш данных фиксированной длины

Формально структура выглядит так:

= (, )

В контексте Jsrsasign алгоритм обычно задаётся строкой (SHA-256, SHA-1, SHA-512), а хеш вычисляется заранее через KJUR.crypto.MessageDigest.

Формирование хеша исходных данных

Перед созданием TSA-запроса данные приводятся к байтовому представлению, после чего вычисляется digest:

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

Хеш обязательно должен быть в бинарно-hex формате, поскольку ASN.1 структура ожидает OCTET STRING.

Структура TimeStampReq в ASN.1

Запрос TSA включает несколько полей:

  • version — версия протокола (обычно 1)
  • messageImprint — хеш и алгоритм
  • nonce — случайное число для защиты от replay-атак
  • certReq — флаг запроса сертификата TSA

Обобщённая структура:

= (v, MI, nonce, certReq)

Генерация запроса с помощью Jsrsasign

Jsrsasign предоставляет высокоуровневый конструктор TSA-запросов через KJUR.asn1.tsp.TimeStampReq.

const tsq = new KJUR.asn1.tsp.TimeStampReq({
  hashAlg: "sha256",
  hashValue: hashHex,
  nonce: "12345678",
  certreq: true
});

Внутри библиотека автоматически:

  • кодирует ASN.1 структуру
  • формирует DER-последовательность
  • обеспечивает корректную сериализацию MessageImprint

Генерация nonce и его роль

Nonce используется для защиты от повторной отправки старых TSA-ответов. Обычно это случайное 64-битное число.

const nonce = Math.floor(Math.random() * 1000000000).toString();

С точки зрения протокола nonce включается в запрос и должен совпасть в ответе TSA.

Кодирование запроса в DER

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

const reqDer = tsq.getContentInfo().getContent().toHex();

Результат — бинарная DER-структура, которую нельзя модифицировать без нарушения ASN.1 целостности.

Подготовка HTTP POST к TSA серверу

TSA обычно принимает запросы с MIME-type:

  • application/timestamp-query

Передача выполняется в бинарном виде или base64 (в зависимости от сервера).

Пример подготовки:

const base64Req = hextob64(reqDer);

HTTP-заголовки:

Content-Type: application/timestamp-query
Accept: application/timestamp-reply

Отправка запроса и обработка ответа

Ответ TSA приходит в формате TimeStampResp, также ASN.1 DER.

fetch("https://tsa.example.com", {
  method: "POST",
  headers: {
    "Content-Type": "application/timestamp-query",
    "Accept": "application/timestamp-reply"
  },
  body: hextorstr(reqDer)
})
.then(res => res.arrayBuffer())

После получения ответа выполняется разбор:

const resp = new KJUR.asn1.tsp.TimeStampResp({
  data: arrayBufferToHex(buffer)
});

Разбор структуры TimeStampResp

Ответ TSA включает:

  • status (успешность запроса)
  • timeStampToken (CMS-подпись с временной меткой)

CMS-структура внутри содержит:

  • signedData
  • signerInfo
  • certificate chain TSA
  • timestamp token

Проверка соответствия хеша

Ключевой этап — сравнение:

  • исходного MessageImprint
  • MessageImprint внутри TimeStampToken

Несовпадение означает подмену или ошибку формирования запроса.

Частые ошибки формирования TSA-запросов

Несоответствие алгоритма

Если TSA ожидает SHA-256, а отправлен SHA-1, сервер отклоняет запрос без криптографической обработки.

Неверный hex-формат

Jsrsasign требует строго hex-строк без префиксов и пробелов.

Отсутствие nonce

Некоторые TSA допускают отсутствие nonce, но это снижает криптографическую стойкость ответа.

Ошибки DER-кодирования

Ручная модификация байтовой строки приводит к невалидному ASN.1 и отказу TSA.

Особенности реализации Jsrsasign

Jsrsasign скрывает низкоуровневую ASN.1 сложность:

  • автоматическая генерация SEQUENCE
  • контроль типов OCTET STRING
  • поддержка RFC 3161 структуры
  • интеграция с crypto-js провайдером

Внутренне библиотека использует KJUR.asn1.ASN1Object для построения дерева объектов, которое затем сериализуется в DER.

Использование альтернативных источников данных

Вместо строки данные могут быть:

  • файл (ArrayBuffer)
  • бинарный поток
  • заранее вычисленный digest

Пример для файла:

const fileBuffer = await file.arrayBuffer();
const md = new KJUR.crypto.MessageDigest({ alg: "sha256" });
md.updateHex(arrayBufferToHex(fileBuffer));
const hashHex = md.digest();

Криптографическая роль TSA-запроса

Формирование запроса не просто техническая операция, а подготовка доказуемой временной привязки данных:

= H() + T_{TSA} +

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