CAdES-T (CMS Advanced Electronic Signatures with Time) представляет собой расширение базового формата CAdES-BES/CAdES-EPES, в котором добавляется доверенная метка времени, подтверждающая существование подписи в конкретный момент.
Ключевая особенность формата заключается в том, что он фиксирует не только факт подписания, но и юридически значимое время, полученное от доверенного сервера меток времени (TSA — Time Stamping Authority).
Основная цель применения CAdES-T:
В контексте Jsrsasign этот формат реализуется через комбинирование CMS-подписи и RFC3161 timestamp token.
CAdES-T строится поверх базовой CMS SignedData структуры:
Ключевой элемент — это поле:
signatureTimeStampTokenОно содержит ASN.1 структуру с ответом TSA.
Jsrsasign предоставляет набор классов для работы с CMS и PKI:
KJUR.crypto.CMS — формирование CMS SignedDataKJUR.asn1.tsp — работа с TimeStamp ProtocolKJUR.crypto.TSPUtil — утилиты для TSAТипичный процесс состоит из двух этапов: создание подписи и добавление timestamp.
Перед добавлением метки времени создаётся обычная CMS подпись:
const cms = new KJUR.crypto.CMS();
const signedData = cms.sign({
content: {
str: "Hello CAdES-T document"
},
certs: [certPEM],
privateKey: privateKeyPEM,
hashAlg: "sha256",
econtentType: "data"
});
На этом этапе формируется CAdES-BES структура.
Для получения метки времени требуется hash от подписанных данных.
Jsrsasign использует DER-encoded SignedData:
const sigHex = signedData.toHex();
const sigHash = KJUR.crypto.Util.hashHex(sigHex, "sha256");
Этот хэш отправляется в TSA как request message imprint.
Jsrsasign поддерживает создание TimeStamp Request:
const tspReq = new KJUR.asn1.tsp.TimeStampReq({
hashAlg: "sha256",
messageImprint: sigHash,
nonce: Math.floor(Math.random() * 1000000),
certReq: true
});
const reqHex = tspReq.getEncodedHex();
Запрос отправляется на TSA сервер через HTTP POST.
Запрос отправляется к TSA endpoint:
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://tsa.example.com/tsp", false);
xhr.setRequestHeader("Content-Type", "application/timestamp-query");
xhr.send(new Uint8Array(KJUR.crypto.Util.hexToBytes(reqHex)));
const tspResponseHex = KJUR.crypto.Util.arrayBufferToHex(xhr.response);
Ответ содержит ASN.1 структуру TimeStampResp.
Из ответа извлекается TimeStampToken:
const tspResp = new KJUR.asn1.tsp.TimeStampResp({
hex: tspResponseHex
});
const tokenHex = tspResp.getTimeStampTokenHex();
Token включает:
После получения токена он добавляется в unsigned attributes подписи:
signedData.addUnsignedAttribute({
attrType: "signatureTimeStampToken",
attrValues: [tokenHex]
});
На этом этапе структура становится CAdES-T.
CMS структура после добавления timestamp включает:
SignedData
version
digestAlgorithms
encapContentInfo
certificates
signerInfos
signedAttributes
signature
unsignedAttributes
RFC3161 token включает ASN.1 структуру:
TSTInfo
Особое значение имеет genTime, фиксирующее момент
времени, подтверждённый TSA.
Проверка включает два уровня:
const cmsVerifier = new KJUR.crypto.CMSVerifier();
const result = cmsVerifier.verify({
cms: signedDataHex,
certs: [certPEM]
});
const tspVerifier = new KJUR.asn1.tsp.TimeStampRespVerifier();
const isValidTSP = tspVerifier.verify({
tsrHex: tokenHex,
dataHash: sigHash
});
Проверяется:
TSA выступает как независимая третья сторона, подтверждающая существование подписи.
Модель доверия:
Критически важно, чтобы TSA использовал:
Если изменён SignedData после формирования hash:
Если TSA не доверен:
Jsrsasign чувствителен к:
Полный процесс можно свести к следующей последовательности:
// 1. CMS подпись
const cms = new KJUR.crypto.CMS();
const signed = cms.sign({
content: { str: "Document" },
certs: [certPEM],
privateKey: privateKeyPEM,
hashAlg: "sha256"
});
// 2. Хэш подписи
const hash = KJUR.crypto.Util.hashHex(signed.getContentInfoHex(), "sha256");
// 3. TSA request
const req = new KJUR.asn1.tsp.TimeStampReq({
hashAlg: "sha256",
messageImprint: hash
});
// 4. Получение ответа TSA
const responseHex = sendToTSA(req.getEncodedHex());
// 5. Извлечение token
const resp = new KJUR.asn1.tsp.TimeStampResp({ hex: responseHex });
const token = resp.getTimeStampTokenHex();
// 6. Добавление в подпись
signed.addUnsignedAttribute({
attrType: "signatureTimeStampToken",
attrValues: [token]
});
Jsrsasign реализует CAdES-T не как отдельный объект, а как расширение CMS-структуры.
Это означает:
Такой подход даёт гибкость, но требует точного понимания ASN.1 модели.
Уровень устойчивости определяется комбинацией:
CAdES-T не защищает от подделки документа, но фиксирует момент существования подписи, что критично для юридических сценариев и архивирования.