SignerInfo: атрибуты и структура

SignerInfo является центральным элементом структуры CMS (Cryptographic Message Syntax), определённой в RFC 5652, и используется для описания информации о подписи конкретного подписанта в подписанном сообщении. В контексте Jsrsasign работа с SignerInfo осуществляется на уровне ASN.1-структур, где каждый экземпляр представляет отдельную цифровую подпись внутри контейнера SignedData.

SignerInfo не существует изолированно: он всегда входит в состав массива SignerInfos, который может содержать несколько подписей, если документ подписан несколькими участниками.


ASN.1-структура SignerInfo

Классическая ASN.1-структура SignerInfo имеет следующий вид:

SignerInfo ::= SEQUENCE {
    version                   INTEGER,
    sid                       SignerIdentifier,
    digestAlgorithm           DigestAlgorithmIdentifier,
    signedAttrs               [0] IMPLICIT SignedAttributes OPTIONAL,
    signatureAlgorithm        SignatureAlgorithmIdentifier,
    signature                 OCTET STRING,
    unsignedAttrs             [1] IMPLICIT UnsignedAttributes OPTIONAL
}

Каждое поле выполняет строго определённую криптографическую и идентификационную функцию.


version

Поле version определяет версию структуры SignerInfo. На практике встречаются два основных значения:

  • 1 — используется при идентификации подписанта через IssuerAndSerialNumber
  • 3 — используется при применении SubjectKeyIdentifier

Jsrsasign автоматически определяет корректное значение в зависимости от типа идентификатора ключа.


sid (SignerIdentifier)

SignerIdentifier определяет, какой сертификат использовался для формирования подписи. Существует два варианта представления:

IssuerAndSerialNumber

Используется связка:

  • Distinguished Name (DN) издателя сертификата
  • Серийный номер сертификата

Такой вариант наиболее распространён в классических PKCS#7 структурах.

SubjectKeyIdentifier

Представляет идентификатор ключа субъекта сертификата. Этот вариант более современный и используется в CMS.

В Jsrsasign этот элемент формируется на основе объекта X509Certificate и внутренних методов извлечения идентификаторов.


digestAlgorithm

Поле digestAlgorithm указывает алгоритм хеширования, применённый к подписываемым данным.

Наиболее распространённые значения:

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

В Jsrsasign алгоритм задаётся через OID, например:

  • 2.16.840.1.101.3.4.2.1 — SHA-256

Важно учитывать, что digestAlgorithm относится не к самой подписи, а к предварительному хешированию данных перед подписанием.


signedAttrs (подписанные атрибуты)

SignedAttributes — это критически важная часть структуры SignerInfo, определяющая данные, которые фактически подписываются.

Особенность обработки

Если signedAttrs присутствуют, то подпись формируется не над исходным контентом, а над DER-кодированным набором атрибутов.

Это означает:

  • Подписывается не документ напрямую
  • Подписывается структурированный набор метаданных

Типичные атрибуты signedAttrs:

  • contentType — тип подписываемого содержимого
  • messageDigest — хеш исходного контента
  • signingTime — время подписи
  • cmsAlgorithmProtection — защита алгоритмов (в современных схемах)

В Jsrsasign signedAttrs формируются через ASN.1 конструкторы и автоматически включаются при создании CMS SignedData при включённой опции атрибутной подписи.


signatureAlgorithm

Поле signatureAlgorithm определяет криптографический алгоритм, используемый для создания цифровой подписи.

Примеры:

  • rsaEncryption
  • ecdsa-with-SHA256
  • rsassaPss (в современных реализациях)

OID алгоритма определяет как сам метод подписи, так и параметры (например, padding для RSA-PSS).

В Jsrsasign выбор алгоритма связан с приватным ключом и явно переданными параметрами при вызове функций подписи.


signature (подпись)

Поле signature содержит фактическое значение цифровой подписи в виде OCTET STRING.

Содержимое зависит от алгоритма:

  • RSA — байтовая строка, результат возведения в степень по модулю
  • ECDSA — DER-кодированная пара (r, s)

Важный момент: в CMS подпись создаётся над DER-кодированным значением signedAttrs (если они используются), а не над исходным документом.

Jsrsasign выполняет двойной уровень обработки:

  1. Формирование хеша
  2. Подпись хеша приватным ключом
  3. Кодирование результата в ASN.1

unsignedAttrs (неподписанные атрибуты)

UnsignedAttributes — это дополнительные атрибуты, которые не входят в область криптографической защиты подписи.

Они могут быть добавлены после формирования подписи и не нарушают её валидность.

Типичные примеры:

  • timestamp token (временная метка TSA)
  • countersignature (контрподпись)
  • архивные метаданные

В Jsrsasign unsignedAttrs обычно добавляются на этапе постобработки CMS структуры.


Формирование SignerInfo в Jsrsasign

В библиотеке Jsrsasign создание SignerInfo происходит через внутренние классы CMS, чаще всего через:

  • KJUR.asn1.cms.SignerInfo
  • KJUR.asn1.cms.SignedData

Процесс формирования включает несколько этапов:

  1. Извлечение сертификата подписанта
  2. Определение идентификатора sid
  3. Выбор digestAlgorithm
  4. Формирование signedAttrs (при необходимости)
  5. Хеширование DER-представления атрибутов
  6. Создание подписи приватным ключом
  7. Сборка ASN.1 SEQUENCE

Особенности кодирования ASN.1

SignerInfo строго следует DER-правилам кодирования:

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

Ошибки в DER-кодировании signedAttrs приводят к невозможности валидации подписи, даже если криптографически подпись корректна.

Jsrsasign использует внутренний ASN.1 encoder для обеспечения совместимости с CMS-совместимыми системами.


Взаимосвязь SignerInfo и SignedData

SignerInfo всегда является частью SignedData:

SignedData
 ├── version
 ├── digestAlgorithms
 ├── encapContentInfo
 ├── certificates
 └── signerInfos → [ SignerInfo, SignerInfo, ... ]

Каждый SignerInfo независим и может использовать:

  • разные алгоритмы подписи
  • разные сертификаты
  • разные наборы атрибутов

Это позволяет реализовывать многоподписные документы с разными криптографическими профилями.


Проверка SignerInfo

Валидация SignerInfo включает несколько уровней:

  • проверка соответствия сертификата sid
  • проверка цепочки доверия
  • проверка digestAlgorithm
  • вычисление хеша signedAttrs
  • проверка подписи signature
  • анализ unsignedAttrs (если применимо)

Jsrsasign предоставляет инструменты для поэтапной проверки, включая извлечение и декодирование ASN.1 структуры SignerInfo для диагностики ошибок.


Типовые ошибки при работе с SignerInfo

На практике встречаются следующие проблемы:

  • несоответствие алгоритма подписи и сертификата
  • нарушение DER-кодирования signedAttrs
  • отсутствие обязательного messageDigest
  • неверный порядок атрибутов
  • использование устаревших алгоритмов (SHA-1)

Каждая из этих ошибок приводит к невозможности криптографической проверки, даже при корректной структуре CMS-контейнера.