В формате CMS (Cryptographic Message Syntax) объект SignedData содержит не только подписи, но и дополнительную криптографическую инфраструктуру, позволяющую проверяющей стороне восстановить цепочку доверия. Ключевым элементом этой структуры являются X.509 сертификаты, которые могут быть включены непосредственно внутрь SignedData.
Сертификаты в SignedData выполняют несколько функций:
В библиотеке Jsrsasign работа с сертификатами в SignedData
реализуется через объектную модель CMS, где сертификаты включаются в
поле certificates.
Внутреннее представление SignedData в Jsrsasign строится вокруг основных компонентов CMS:
contentInfo — данные, которые подписываютсяsignerInfos — информация о подписи (подписанты)certificates — список X.509 сертификатовcrls — списки отзыва сертификатов (опционально)Добавление сертификатов происходит на этапе генерации CMS-структуры или при ручной сборке объекта.
В Jsrsasign формирование SignedData обычно выполняется через
KJUR.asn1.cms.SignedData.
Основной механизм включения сертификатов — передача массива PEM/DER
сертификатов в параметр certs.
const cms = new KJUR.asn1.cms.SignedData({
content: {
type: "data",
data: "Hello CMS"
},
certs: [
"-----BEGIN CERTIFICATE-----\nMIIC...IDAQAB\n-----END CERTIFICATE-----"
],
signerInfos: [{
sid: {
issuer: {str: "/C=US/O=Example/CN=Test CA"},
serial: {hex: "01"}
},
hashAlg: "sha256",
sattrs: {
signingTime: new Date()
},
sigAlg: "SHA256withRSA",
prvkey: privateKeyPem
}]
});
const cmsOutput = cms.getContentInfo();
В реальных сценариях в SignedData включается не один сертификат, а целая цепочка:
const cms = new KJUR.asn1.cms.SignedData({
content: {
type: "data",
data: "Document payload"
},
certs: [
signerCertPem,
intermediateCertPem,
rootCertPem
],
signerInfos: [{
sid: {
issuer: {str: "/C=US/O=Example/CN=Intermediate CA"},
serial: {hex: "02"}
},
hashAlg: "sha256",
sigAlg: "SHA256withECDSA",
prvkey: privateKeyPem
}]
});
В CMS SignedData сертификаты кодируются как набор ASN.1 объектов типа
CertificateSet.
Jsrsasign автоматически преобразует PEM в DER и помещает их в структуру:
CertificateSet ::= SET OF CertificateКаждый сертификат хранится независимо от подписантов, что позволяет:
Подписант (SignerInfo) не содержит сам сертификат.
Вместо этого используется идентификатор:
Этот механизм называется Signer Identifier (SID).
sid: {
issuer: {str: "/C=US/O=Example/CN=CA"},
serial: {hex: "0A12BC"}
}
При проверке подписи система ищет соответствующий сертификат в
массиве certs.
Jsrsasign поддерживает оба формата:
При передаче в certs:
Пример преобразования:
const x509 = new X509();
x509.readCertPEM(certPem);
const derHex = x509.hex;
В некоторых сценариях SignedData уже сформирован, но требуется дополнить его сертификатами. Это реализуется через декодирование и повторную сборку структуры CMS.
const cmsObj = KJUR.asn1.cms.CMSUtil.parse(cmsPem);
cmsObj.contentInfo.content.certificates.push(newCertPem);
const updatedCms = cmsObj.getContentInfo();
Если поле certs не задано:
Это характерно для минималистичных подписей, где сертификаты передаются отдельно.
SignedData может содержать несколько SignerInfo. В этом
случае:
certssignerInfos: [
signerA,
signerB
],
certs: [
certA,
certB,
intermediateCA
]
При работе с внешними CMS-системами важно учитывать:
Jsrsasign не накладывает ограничений и позволяет включать произвольный набор сертификатов в соответствии с требованиями протокола.
Типовые проблемы возникают при:
В таких случаях проверка подписи завершается ошибкой построения цепочки доверия, а не ошибкой криптографической операции.