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-подписи включает несколько последовательных этапов:
Библиотека 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 подпись без метки времени.
Для запроса временной метки требуется хэш уже сформированной подписи. Обычно используется SHA-256:
const sigHex = cms.getSignatureValueHex();
const hash = KJUR.crypto.Util.hashHex(sigHex, "sha256");
Хэш становится основой для формирования запроса TimeStampReq.
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();
Структура запроса включает:
Запрос отправляется на 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);
Ответ содержит структуру TimeStampResp, внутри которой
находится TimeStampToken.
const tspResp = new KJUR.crypto.TSResponse({ data: respHex });
const status = tspResp.getStatus();
const tsTokenHex = tspResp.getTimeStampToken();
В случае успешного ответа статус должен быть granted или
grantedWithMods.
TimeStampToken добавляется как unsigned attribute в SignedData.
В Jsrsasign это выполняется через модификацию структуры CMS:
cms.addUnsignedAttribute({
attrType: "1.2.840.113549.1.9.16.2.14",
attrValue: tsTokenHex
});
После добавления атрибута структура подписи обновляется до уровня CAdES-T.
После внедрения метки времени CMS структура пересобирается:
const cadestHex = cms.getContentInfo().getContentHex();
Результат представляет собой CMS SignedData с включённым RFC 3161 TimeStampToken.
Внутри ASN.1 токена содержатся следующие элементы:
tstInfo
signature TSA
certificates TSA (опционально)
Эта структура обеспечивает доказательство существования подписи в
момент времени genTime.
Проверка включает несколько этапов:
Пример извлечения данных:
const parsed = new KJUR.asn1.cms.CMSSignedData({ cmshex: cadestHex });
const tsAttr = parsed.getUnsignedAttribute(
"1.2.840.113549.1.9.16.2.14"
);
При практической интеграции встречаются характерные ошибки:
Особое внимание требуется к стабильности входных данных CMS перед вычислением messageImprint.
Большинство TSA реализуют RFC 3161 через HTTP(S). Формат запроса и ответа остаётся неизменным, что позволяет переключать провайдеров без изменения логики подписи.
Типовая конфигурация:
Jsrsasign работает на уровне ASN.1 и позволяет:
При этом отсутствует жёсткая абстракция CAdES-уровней, что требует ручного управления атрибутами подписи и структуры CMS.
Unsigned attributes в CMS представлены как набор ASN.1 объектов:
UnsignedAttributes ::= SET OF Attribute
Каждый атрибут включает:
TimeStampToken всегда помещается как единственное значение в наборе
attrValues.
Ключевой момент CAdES-T — использование именно значения подписи (SignatureValue), а не всего CMS контейнера.
const sigValue = cms.getSignatureValueHex();
const imprint = KJUR.crypto.Util.hashHex(sigValue, "sha256");
Нарушение этого правила приводит к невозможности валидации TSA токена.
CAdES-T является базой для более высоких уровней:
Метка времени фиксирует момент подписи и позволяет строить долговременную верификацию цепочки доверия.