CMS (Cryptographic Message Syntax, RFC 5652) в контексте JavaScript-библиотеки jsrsasign представляется в виде структуры PKCS
CMS-подпись в формате PKCS#7 включает несколько ключевых компонентов:
В jsrsasign эта структура обрабатывается через ASN.1 парсинг и объектные модели PKCS#7/CMS.
Для работы с CMS используется пакет jsrsasign:
import * as KJUR from 'jsrsasign';
или в браузерной среде:
<script src="jsrsasign-all-min.js"></script>
Основные пространства имён:
KJUR.cryptoKJUR.asn1.cmsKEYUTILX509CMS-подпись обычно поступает в формате PEM или DER. В jsrsasign она декодируется следующим образом:
const cmsData = "-----BEGIN PKCS7-----...-----END PKCS7-----";
const p7 = new KJUR.crypto.PKCS7({ data: cmsData });
При необходимости можно работать с DER:
const p7 = new KJUR.crypto.PKCS7({ hex: cmsHex });
После парсинга структура становится доступной для анализа:
p7.type — тип контейнераp7.signerInfo — информация о подписиp7.certificates — список сертификатовp7.content — подписанные данныеCMS может содержать либо встроенные данные, либо detached signature.
const content = p7.getContent();
Если подпись detached, исходные данные должны быть переданы отдельно:
const data = "original message";
Основной этап — криптографическая верификация подписи.
const result = p7.verify({ data: data });
Возвращаемое значение:
true — подпись корректнаfalse — подпись недействительнаПодпись CMS не имеет смысла без проверки сертификата.
Извлечение сертификата:
const certs = p7.certificates;
const signerCert = certs[0];
Парсинг X.509:
const x509 = new X509();
x509.readCertHex(signerCert);
Проверка срока действия:
const notBefore = x509.getNotBefore();
const notAfter = x509.getNotAfter();
Для полноценной валидации необходимо проверить цепочку сертификатов:
const caCert = new X509();
caCert.readCertPEM(caPem);
const isValid = x509.verifySignature(caCert);
В реальных CMS-потоках цепочка может содержать несколько промежуточных сертификатов:
p7.certificates.forEach(certHex => {
const cert = new X509();
cert.readCertHex(certHex);
});
CMS поддерживает различные алгоритмы:
Определение алгоритма:
const alg = p7.signerInfo.signatureAlgorithm.name;
В CMS часто используются authenticated attributes:
const attrs = p7.signerInfo.authenticatedAttributes;
Ключевые атрибуты:
Проверка digest:
const md = KJUR.crypto.Util.hashString(data, "sha256");
Процесс включает несколько этапов:
const p7 = new KJUR.crypto.PKCS7({ data: cmsPem });
const data = p7.getContent();
const ok = p7.verify({ data: data });
const cert = new X509();
cert.readCertHex(p7.certificates[0]);
const trusted = cert.verifySignature(rootCert);
Detached CMS не содержит данных внутри структуры:
const p7 = new KJUR.crypto.PKCS7({ data: cmsSignature });
const ok = p7.verify({ data: externalData });
Ошибка в передаче данных приводит к провалу верификации даже при корректной подписи.
CMS часто приходит в бинарном виде:
const p7 = new KJUR.crypto.PKCS7({ hex: cmsDerHex });
Конвертация PEM → HEX:
const hex = KJUR.asn1.ASN1Util.pemToHex(pem);
SignerInfo содержит идентификатор подписанта:
const signer = p7.signerInfo;
const issuer = signer.issuer;
const serial = signer.serialNumber;
Эти данные используются для сопоставления с сертификатом.
Типичные причины отказа проверки:
Получение диагностической информации:
try {
p7.verify({ data: data });
} catch (e) {
console.log(e.message);
}
CMS поддерживает множественные подписи:
p7.signerInfos.forEach(si => {
const alg = si.signatureAlgorithm.name;
});
Каждая подпись проверяется независимо.
Если CMS содержит embedded content:
const content = p7.contentInfo.content;
Данные могут быть в формате OCTET STRING и требовать дополнительной декодировки.
Добавление доверенного корневого сертификата:
const root = new X509();
root.readCertPEM(rootPem);
Используется при построении trust chain.
CMS-верификация опирается на:
const cms = new KJUR.crypto.PKCS7({ data: cmsPem });
const content = cms.getContent();
const validSignature = cms.verify({ data: content });
const signerCertHex = cms.certificates[0];
const cert = new X509();
cert.readCertHex(signerCertHex);
const certValid = cert.verifySignature(rootCert);
Каждый этап верификации должен завершаться успешно для признания CMS-подписи валидной.