Практика: добавление метки времени (CAdES-T)

CAdES-T (CMS Advanced Electronic Signatures with Time) представляет собой уровень усовершенствованной электронной подписи, в котором к базовой подписи добавляется доверенная метка времени, полученная от службы TSA (Time Stamping Authority). Основная цель этого уровня — зафиксировать момент существования подписи независимо от времени системы подписанта и обеспечить доказуемость существования подписи в конкретный момент времени.

В контексте CMS (Cryptographic Message Syntax) метка времени добавляется как неподписанный атрибут подписи, содержащий RFC 3161 time-stamp token.

Ключевой идентификатор атрибута:

id-aa-signatureTimeStampToken = 1.2.840.113549.1.9.16.2.14

Архитектура процесса добавления метки времени

Процесс формирования CAdES-T-подписи включает несколько последовательных этапов:

  1. Формирование CMS подписи (CAdES-BES уровень)
  2. Вычисление хэш-значения подписи
  3. Формирование запроса к TSA (TimeStampReq)
  4. Получение ответа TSA (TimeStampResp)
  5. Извлечение TimeStampToken
  6. Встраивание токена в структуру CMS как unsigned attribute

Формирование базовой CMS подписи в Jsrsasign

Библиотека Jsrsasign предоставляет инструменты для создания CMS SignedData структуры через пространство KJUR.asn1.cms.

Формирование базовой подписи:

const cms = new KJUR.asn1.cms.CMSSignedData();

cms.setContent({
  type: "text",
  content: "Hello CMS"
});

cms.sign({
  cert: certPem,
  privateKey: privateKeyPem,
  digestAlg: "sha256"
});

const cmsHex = cms.getContentInfo().getContentHex();

На этом этапе создаётся CAdES-BES подпись без метки времени.


Вычисление хэша подписи для TSA

Для запроса временной метки требуется хэш уже сформированной подписи. Обычно используется SHA-256:

const sigHex = cms.getSignatureValueHex();

const hash = KJUR.crypto.Util.hashHex(sigHex, "sha256");

Хэш становится основой для формирования запроса TimeStampReq.


Формирование запроса к TSA (RFC 3161)

Jsrsasign содержит поддержку RFC 3161 через KJUR.crypto утилиты.

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

const tsq = new KJUR.crypto.TSRequest();

tsq.setMessageImprint({
  hashAlg: "sha256",
  hashValue: hash
});

tsq.setNonce("12345678");
tsq.setCertReq(true);

const reqHex = tsq.getEncodedHex();

Структура запроса включает:

  • алгоритм хэширования
  • значение хэша подписи
  • nonce для защиты от повторов
  • флаг запроса сертификата TSA

Отправка запроса в TSA

Запрос отправляется на HTTP endpoint службы времени. Обычно используется POST с содержимым DER/ASN.1.

const xhr = new XMLHttpRequest();
xhr.open("POST", "https://tsa.example.com/timestamp", false);
xhr.setRequestHeader("Content-Type", "application/timestamp-query");

xhr.send(KJUR.lang.String.fromHex(reqHex));

const respHex = KJUR.lang.String.fromCharCodeArray(xhr.response);

Разбор ответа TSA

Ответ содержит структуру TimeStampResp, внутри которой находится TimeStampToken.

const tspResp = new KJUR.crypto.TSResponse({ data: respHex });

const status = tspResp.getStatus();
const tsTokenHex = tspResp.getTimeStampToken();

В случае успешного ответа статус должен быть granted или grantedWithMods.


Встраивание метки времени в CMS (CAdES-T)

TimeStampToken добавляется как unsigned attribute в SignedData.

В Jsrsasign это выполняется через модификацию структуры CMS:

cms.addUnsignedAttribute({
  attrType: "1.2.840.113549.1.9.16.2.14",
  attrValue: tsTokenHex
});

После добавления атрибута структура подписи обновляется до уровня CAdES-T.


Итоговая сборка CAdES-T подписи

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

const cadestHex = cms.getContentInfo().getContentHex();

Результат представляет собой CMS SignedData с включённым RFC 3161 TimeStampToken.


Структура TimeStampToken

Внутри ASN.1 токена содержатся следующие элементы:

  • tstInfo

    • policy
    • messageImprint
    • serialNumber
    • genTime
  • signature TSA

  • certificates TSA (опционально)

Эта структура обеспечивает доказательство существования подписи в момент времени genTime.


Проверка CAdES-T подписи

Проверка включает несколько этапов:

  1. Валидация CMS подписи (CAdES-BES)
  2. Проверка сертификата TSA
  3. Проверка корректности TimeStampToken
  4. Сравнение хэша подписи с messageImprint в токене
  5. Проверка доверенной цепочки TSA

Пример извлечения данных:

const parsed = new KJUR.asn1.cms.CMSSignedData({ cmshex: cadestHex });

const tsAttr = parsed.getUnsignedAttribute(
  "1.2.840.113549.1.9.16.2.14"
);

Типовые проблемы при работе с TSA

При практической интеграции встречаются характерные ошибки:

  • несовпадение алгоритмов хэширования между подписью и TSA
  • неверное кодирование DER при отправке запроса
  • отсутствие доверенного сертификата TSA
  • использование неподдерживаемого endpoint
  • изменение байтов подписи до расчёта хэша

Особое внимание требуется к стабильности входных данных CMS перед вычислением messageImprint.


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

Большинство TSA реализуют RFC 3161 через HTTP(S). Формат запроса и ответа остаётся неизменным, что позволяет переключать провайдеров без изменения логики подписи.

Типовая конфигурация:

  • URL TSA endpoint
  • алгоритм SHA-256
  • политика timestamp (OID TSA policy)
  • опциональная проверка сертификата TSA

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

Jsrsasign работает на уровне ASN.1 и позволяет:

  • формировать CMS структуры без внешних библиотек
  • генерировать RFC 3161 запросы
  • разбирать TSA ответы
  • модифицировать SignedData вручную

При этом отсутствует жёсткая абстракция CAdES-уровней, что требует ручного управления атрибутами подписи и структуры CMS.


Форматирование unsigned attribute в CMS

Unsigned attributes в CMS представлены как набор ASN.1 объектов:

UnsignedAttributes ::= SET OF Attribute

Каждый атрибут включает:

  • attrType (OID)
  • attrValues (SET OF ANY)

TimeStampToken всегда помещается как единственное значение в наборе attrValues.


Хэширование подписи перед TSA запросом

Ключевой момент CAdES-T — использование именно значения подписи (SignatureValue), а не всего CMS контейнера.

const sigValue = cms.getSignatureValueHex();
const imprint = KJUR.crypto.Util.hashHex(sigValue, "sha256");

Нарушение этого правила приводит к невозможности валидации TSA токена.


Связь CAdES-T и дальнейших уровней CAdES

CAdES-T является базой для более высоких уровней:

  • CAdES-C (добавление ссылок на сертификаты и OCSP)
  • CAdES-X Long
  • CAdES-A (архивный уровень)

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