Практика: встраивание метки времени в CMS

CMS (Cryptographic Message Syntax) в контексте JavaScript чаще всего реализуется через библиотеку Jsrsasign, где поддерживаются как базовые операции подписи, так и расширенные механизмы ASN.1 структур, включая добавление атрибутов и интеграцию временных меток RFC3161.

CMS SignedData представляет собой контейнер, включающий:

  • подписываемые данные (encapContentInfo)
  • сертификаты подписанта
  • подпись (signature value)
  • набор подписанных атрибутов (signed attributes)
  • набор неподписанных атрибутов (unsigned attributes)

Метка времени в криптографическом смысле обычно реализуется через RFC3161 Time-Stamp Token (TST), который добавляется в CMS как unsigned attribute. Это принципиально важно: временная метка не изменяет подпись, а лишь подтверждает факт существования подписи в конкретный момент времени.

Подготовка окружения Jsrsasign

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

Типовая инициализация:

const jsrsasign = require("jsrsasign");

const KJUR = jsrsasign.KJUR;
const hextool = jsrsasign.hextool;

Для CMS используется модуль:

  • KJUR.asn1.cms.SignedData

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

Первым этапом создаётся стандартная CMS структура без временной метки.

const msg = "Document for signing";

const cmsSigned = KJUR.asn1.cms.CMSUtil.sign(
  msg,
  {
    cert: CERT_PEM,
    key: PRIVATE_KEY_PEM,
    alg: "SHA256withRSA"
  }
);

Результат — PKCS#7/CMS SignedData, содержащий подпись и сертификат.

Внутри структуры формируется:

  • digest алгоритм SHA-256
  • signature value RSA
  • signed attributes (messageDigest, signingTime)

Получение RFC3161 временной метки

Для добавления timestamp требуется TSA (Time Stamping Authority). В Jsrsasign реализована работа через KJUR.asn1.tsp.

Запрос формируется как TSP Request:

const tspReq = new KJUR.asn1.tsp.TimeStampReq({
  hashAlg: "sha256",
  imprint: KJUR.crypto.Util.hashString(msg, "sha256")
}).getContentInfo();

Далее запрос отправляется на TSA сервер:

const xhr = new XMLHttpRequest();
xhr.open("POST", TSA_URL, false);
xhr.setRequestHeader("Content-Type", "application/timestamp-query");
xhr.send(tspReq);

Ответ представляет собой DER-структуру TimeStampResp.

Разбор TimeStampToken

Полученный ответ декодируется:

const tspResp = new KJUR.asn1.tsp.TimeStampResp({
  str: jsrsasign.ASN1HEX.parseHex(xhr.responseText)
});

const token = tspResp.getTimeStampToken();

TimeStampToken содержит:

  • genTime (время фиксации)
  • messageImprint (хеш исходных данных)
  • TSA certificate
  • signature TSA

Встраивание временной метки в CMS

CMS поддерживает добавление unsigned attributes. В Jsrsasign это реализуется через модификацию структуры SignedData.

Ключевой атрибут:

id-aa-signatureTimeStampToken (1.2.840.113549.1.9.16.2.14)

Формирование ASN.1 структуры атрибута:

const sigTstAttr = {
  type: "1.2.840.113549.1.9.16.2.14",
  value: token
};

Далее атрибут добавляется в CMS:

const cms = new KJUR.asn1.cms.SignedData({
  content: msg,
  certs: [CERT_PEM],
  signerInfos: [
    {
      signKey: PRIVATE_KEY_PEM,
      hashAlg: "sha256",
      unsignedAttrs: [sigTstAttr]
    }
  ]
});

В результате формируется CMS, где подпись дополнена временной меткой без нарушения целостности signed attributes.

Криптографическая модель интеграции

Важно понимать порядок вычислений:

  1. Вычисляется digest исходного сообщения
  2. Формируется SignedAttributes (включая messageDigest)
  3. Вычисляется подпись
  4. Отправляется хеш в TSA
  5. Получается TimeStampToken
  6. Token добавляется как unsigned attribute

Таким образом, временная метка подтверждает существование подписи после её формирования.

Проверка CMS с временной меткой

Проверка выполняется через CMS.verify:

const result = KJUR.asn1.cms.CMSUtil.verify({
  cms: cmsSigned,
  certs: [CERT_PEM]
});

Дополнительно извлекается unsigned attribute:

const attrs = result.signerInfos[0].unsignedAttrs;

Декодирование timestamp token:

const tst = new KJUR.asn1.tsp.TimeStampToken({
  hex: attrs[0].value
});

Проверяются:

  • валидность подписи TSA
  • соответствие messageImprint
  • корректность времени

Практическая архитектура CMS с меткой времени

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

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

Типичная схема обработки:

[Документ]
   ↓
[CMS подпись]
   ↓
[Хеш отправляется в TSA]
   ↓
[Получение TimeStampToken]
   ↓
[Добавление в CMS unsigned attributes]
   ↓
[Финальный CMS контейнер]

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

Jsrsasign не абстрагирует TSA как высокоуровневый сервис, поэтому:

  • HTTP-запрос к TSA реализуется вручную
  • ASN.1 структуры собираются явно
  • контроль за OID выполняется разработчиком

Ключевые OID:

  • CMS SignedData: 1.2.840.113549.1.7.2
  • timestamp token: 1.2.840.113549.1.9.16.2.14
  • SHA-256: 2.16.840.1.101.3.4.2.1

Расширенные сценарии использования

Множественные временные метки

В CMS допускается несколько unsigned attributes, что позволяет:

  • добавлять повторные timestamps для продления доказательной силы
  • фиксировать цепочку нотариальных подтверждений

Архивная подпись

При длительном хранении документа добавляются:

  • timestamp подписи
  • timestamp сертификата
  • timestamp OCSP ответа

Ошибки интеграции и особенности обработки

Типичные проблемы:

  • несоответствие hash между CMS и TSA request
  • неправильная DER кодировка TimeStampToken
  • отсутствие расширений TSA certificate
  • некорректная обработка ASN.1 SET OF атрибутов

Корректная реализация требует строгого соблюдения:

  • DER encoding (не BER)
  • точного совпадения digest алгоритма
  • неизменности signed attributes после подписи

Проверка целостности временной метки

Алгоритм проверки:

  1. Извлечь signature value из CMS
  2. Пересчитать hash исходных данных
  3. Проверить messageImprint в TST
  4. Проверить подпись TSA сертификатом
  5. Сравнить genTime с политикой доверия

Любое расхождение делает CMS недействительным в юридическом смысле.