Формирование SignedData

CMS (Cryptographic Message Syntax) в реализации Jsrsasign представляет собой ASN.1-структуру, предназначенную для создания подписанных сообщений в формате PKCS#7 / CMS. Основной объект для формирования подписи — SignedData, который включает в себя:

  • подписываемый контент (encapContentInfo или detached mode)
  • алгоритмы хеширования и подписи
  • сертификаты подписанта
  • информацию о подписантах (SignerInfo)
  • дополнительные атрибуты (signed attributes)

В Jsrsasign работа с CMS реализована через пространство имён KJUR.asn1.cms, а также вспомогательные криптографические классы KJUR.crypto.


Подготовка криптографической базы для формирования подписи

Перед созданием SignedData требуется подготовить ключевой материал:

  • приватный ключ RSA или EC
  • сертификат X.509
  • алгоритм подписи (например, SHA256withRSA)

Типичный набор объектов:

const kp = KEYUTIL.generateKeypair("RSA", 2048);
const prvKey = kp.prvKeyObj;
const pubKey = kp.pubKeyObj;

Формирование самоподписанного сертификата (для тестовых сценариев):

const cert = new KJUR.asn1.x509.Certificate({
  version: 3,
  serial: {int: 1},
  sigalg: "SHA256withRSA",
  issuer: [{C: "RU", O: "Test"}],
  notbefore: "230101000000Z",
  notafter: "260101000000Z",
  subject: [{C: "RU", O: "Test"}],
  sbjpubkey: pubKey
});
cert.sign(prvKey, "SHA256withRSA");
const pemCert = cert.getPEM();

Формирование подписываемого контента

CMS SignedData может работать в двух режимах:

  • attached (encapsulated content) — данные включаются в структуру
  • detached — данные не включаются, хранится только хэш

Пример контента:

const content = "Hello CMS SignedData";

Для CMS используется OCTET STRING обёртка:

const oids = KJUR.asn1.cms.CMSUtil;

Создание SignedData через KJUR.asn1.cms.SignedData

Основной класс формирования структуры:

const sd = new KJUR.asn1.cms.SignedData({
  content: {
    type: "data",
    content: content
  },
  certs: [pemCert],
  signerInfos: [{
    signerCert: pemCert,
    digestAlg: "sha256",
    signedAttrs: {
      signingTime: new Date(),
      contentType: "data",
      messageDigest: null
    }
  }]
});

Подпись контента и вычисление SignerInfo

SignerInfo содержит критически важные параметры:

  • IssuerAndSerialNumber
  • digestAlgorithm
  • signatureAlgorithm
  • signedAttributes
  • signatureValue

В Jsrsasign вычисление подписи выполняется автоматически при вызове sign().

Пример явного формирования подписи:

const signer = new KJUR.crypto.Signature({
  alg: "SHA256withRSA"
});
signer.init(prvKey);
signer.updateString(content);
const signatureHex = signer.sign();

Однако в CMS уровень абстракции выше — подпись инкапсулируется внутри SignedData.


Signed Attributes и их роль

Signed attributes формируют защищённую часть подписи. Они хэшируются и включаются в вычисление подписи.

Основные атрибуты:

  • content-type
  • message-digest
  • signing-time

Пример формирования:

signedAttrs: {
  signingTime: new Date(),
  contentType: "data",
  messageDigest: null
}

Jsrsasign автоматически вычисляет messageDigest, если он не задан вручную.


Работа с сертификатами внутри SignedData

Поле certificates включает:

  • сертификат подписанта
  • цепочку доверия (если есть)

Формат PEM передаётся напрямую:

certs: [pemCert]

В ASN.1 структурах это превращается в CertificateSet.


Генерация финальной CMS структуры

После настройки всех параметров происходит построение DER-структуры:

const cmsDer = sd.getContentInfo().getContent().toString("hex");

Или получение PEM/BASE64 представления:

const cmsB64 = hextob64(cmsDer);

Detached SignedData (отделённая подпись)

При использовании detached режима содержимое не встраивается:

const sd = new KJUR.asn1.cms.SignedData({
  content: {
    type: "data",
    content: ""
  },
  detached: true,
  certs: [pemCert],
  signerInfos: [{
    signerCert: pemCert,
    digestAlg: "sha256"
  }]
});

В этом случае хэш считается отдельно и используется только для проверки подписи.


Алгоритмы хеширования и подписи

Jsrsasign поддерживает набор стандартных алгоритмов:

  • SHA-1 (устаревший)
  • SHA-256 (основной)
  • SHA-384
  • SHA-512

Комбинации:

  • SHA256withRSA
  • SHA256withECDSA
  • SHA512withRSA

Указание алгоритма:

digestAlg: "sha256"

ASN.1 структура SignedData

Финальная структура CMS SignedData включает:

  • version
  • digestAlgorithms
  • encapContentInfo
  • certificates (optional)
  • crls (optional)
  • signerInfos

В Jsrsasign это автоматически сериализуется в DER через ASN.1 encoder.


Пример полного цикла формирования SignedData

const kp = KEYUTIL.generateKeypair("RSA", 2048);

const cert = new KJUR.asn1.x509.Certificate({
  version: 3,
  serial: {int: 1},
  sigalg: "SHA256withRSA",
  issuer: [{C: "RU", O: "Org"}],
  subject: [{C: "RU", O: "Org"}],
  sbjpubkey: kp.pubKeyObj
});
cert.sign(kp.prvKeyObj, "SHA256withRSA");

const pemCert = cert.getPEM();

const sd = new KJUR.asn1.cms.SignedData({
  content: {
    type: "data",
    content: "Test message"
  },
  certs: [pemCert],
  signerInfos: [{
    signerCert: pemCert,
    digestAlg: "sha256",
    signedAttrs: {
      signingTime: new Date(),
      contentType: "data"
    }
  }]
});

const cmsHex = sd.getContentInfo().getContent().toHex();

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

  • автоматическое построение ASN.1 дерева
  • поддержка detached и attached подписей
  • автоматическое вычисление digest
  • интеграция с X.509 сертификатами
  • генерация CMS без внешних зависимостей

Типичные ошибки при формировании SignedData

  • отсутствие корректного сертификата подписанта
  • несоответствие алгоритма подписи и ключа
  • ручное неправильное формирование messageDigest
  • несогласованность detached/attached режима
  • нарушение структуры signed attributes

Контроль целостности структуры CMS

Проверка SignedData включает:

  • проверку подписи через публичный ключ
  • сверку messageDigest
  • проверку сертификата в цепочке доверия
  • анализ signedAttributes на соответствие стандарту CMS

В Jsrsasign проверка выполняется через KJUR.crypto.CMSVerifier:

const result = KJUR.crypto.CMSVerifier.verify({
  cms: cmsB64,
  certs: [pemCert]
});