SignedData: что содержит электронная подпись ГОСТ¶
Одна подпись — несколько объектов¶
Человек обычно видит два файла: документ и подпись. Для CMS это означает гораздо более богатую структуру. SignedData может нести содержимое, сертификаты, алгоритмы хэша и по одному SignerInfo на каждого подписанта. Каждый SignerInfo указывает подписанта, алгоритм подписи, её значение и связанные атрибуты.
ContentInfo → SignedData
├── digestAlgorithms
├── encapContentInfo (документ либо ссылка на detached)
├── certificates (могут отсутствовать)
├── crls (могут отсутствовать)
└── signerInfos
├── sid (идентификация подписанта)
├── digestAlgorithm
├── signedAttrs (если предусмотрены)
├── signatureAlgorithm
├── signature
└── unsignedAttrs (если имеются)
certificates и crls — необязательные наборы. Поэтому контейнер, в котором отсутствует сертификат, не обязательно повреждён: сертификат мог передаваться отдельно или находиться у проверяющей стороны.
Что подписывает SignerInfo¶
При наличии signedAttrs подпись вычисляется по DER-кодированию набора подписанных атрибутов согласно RFC 5652, а не просто по исходным байтам документа. Один из атрибутов — messageDigest: в нём закреплён хэш содержимого. Проверяющий должен связать его с реальным документом, а затем проверить подпись над правильным представлением подписанных атрибутов.
Это объясняет важную диагностическую ситуацию: содержимое может быть неизменным, но неверная сериализация signedAttrs приводит к ошибке проверки подписи. Нельзя «нормализовать» атрибуты произвольным способом перед проверкой.
При отсутствии signedAttrs последовательность проверки и подписываемые данные отличаются. Не следует смешивать две схемы без чтения правил CMS.
Гостовская подпись и хэш¶
| Профиль | Алгоритм подписи | Хэш | Длина подписи в CMS |
|---|---|---|---|
| Исторический RFC 4490 | ГОСТ Р 34.10-2001 | ГОСТ Р 34.11-94 | 64 байта |
| Проект ТК26, 2014 | ГОСТ Р 34.10-2012/256 | Стрибог-256 | 64 байта |
| Проект ТК26, 2014 | ГОСТ Р 34.10-2012/512 | Стрибог-512 | 128 байт |
Для этих представлений используются две компоненты s и r. Проверка в криптографической библиотеке должна учитывать порядок и размер байтов. В разных API приложение может получать уже разобранную структуру вместо непосредственно записанной CMS-строки.
Контейнер не равен доверию¶
Рассмотрим три разных результата проверки:
- Структура декодируется. ASN.1 согласован настолько, что можно прочитать
SignerInfo. - Криптографическая подпись верна. Значение подписи соответствует содержимому, подписанным атрибутам и ключу подписанта.
- Подписант признаётся доверенным. Проверена цепочка X.509, правила использования сертификата, политика доверия и статус отзыва по принятой процедуре.
Третье утверждение нельзя вывести только из первых двух. Например, файл может иметь безупречную цифровую подпись от самоподписанного, но неизвестного организации сертификата.
Источники особенно важны при документообороте: ГОСТ-алгоритм — математический инструмент, CMS — формат, сертификат — привязка публичного ключа к субъекту, а правила юридически значимой проверки — отдельный уровень.
Частые ошибки при анализе¶
| Наблюдение | Возможное объяснение |
|---|---|
| Подписанный документ нельзя открыть без исходного файла | Отсоединённый тип содержимого |
| Программа видит SignerInfo, но не подписанта | Отсутствует сертификат или не найден подходящий sid |
| Подпись не проходит после пересохранения документа | Изменились подписанные байты |
| Извлечённый хэш совпадает, а подпись отвергается | Проблема параметров подписи, подписанных атрибутов или ключа |
| Подпись верна, но показана ошибка доверия | Цепочка X.509, срок, назначение, CRL/OCSP или политика |
| Старый ГОСТ 2001 распознан, новый ГОСТ 2012 — нет | Реализация понимает исторические OID, но не современный профиль |
Для проверки прикладной реализацией в НАЙС.ОС используйте OpenSSL CMS либо GPGSM. Примеры здесь поясняют принцип и не заменяют процедуру проверки по доверенной политике.
Полная последовательность проверки SignedData¶
[Файл / отдельный документ]
│
▼
[1. Разобрать ASN.1 ContentInfo → SignedData]
│
▼
[2. Найти SignerInfo + документ + сертификат]
│
▼
[3. Вычислить messageDigest исходного eContent]
│
▼
[4. Проверить signedAttrs / contentType / DER]
│
▼
[5. Проверить ГОСТ Р 34.10 по открытому ключу]
│
▼
[6. Проверить X.509: цепочку, EKU/KU, срок, отзыв]
│
▼
[7. Применить политику алгоритмов, времени и подписанта]
│
▼
[Принято или отказ + отдельная причина]
Шаги 3–4 определены в RFC 5652 §5.4–5.6. Если присутствуют signedAttrs, messageDigest должен соответствовать реальному содержимому, а подпись вычисляется по особому DER-представлению набора атрибутов согласно правилам CMS. Нельзя просто подпихивать в криптобиблиотеку байты messageDigest из контейнера как заведомо достоверные.
Несколько подписантов и изменение состава подписей¶
SignedData.signerInfos — SET OF, так что контейнер может содержать несколько SignerInfo с независимыми сертификатами и алгоритмами. Нужна явная политика: требуется успех всех подписей или достаточно одной? Можно ли удалить одного подписанта без отказа? Для защиты от понижения стойкости в многоподписных сценариях существует RFC 5752, описывающий механизм связывания подписей. Но поддержку конкретного атрибута нужно проверять у используемого ПО.
Атрибуты: что важно различать¶
| Поле | Подписано криптографически? | Что означает |
|---|---|---|
signedAttrs |
Да, при наличии и корректной DER-кодировке | Метаданные, связанные с подписью |
messageDigest внутри signedAttrs |
Да, но нужно сравнить с хэшем документа | Привязка подписи к содержимому |
contentType внутри signedAttrs |
Да; должен совпасть с типом содержимого | Защита от подмены вида содержимого |
signingTime |
При наличии в signedAttrs — да |
Заявленное время подписанта, не доверенная отметка времени |
unsignedAttrs |
Нет, не покрыто этой подписью | Метаданные, которые требуют отдельной проверки |
certificates в SignedData |
Не становятся доверенными автоматически | Материал для построения цепочки |
Доверенная отметка времени — дополнительная проверяемая сущность по RFC 3161. Поле signingTime само по себе не доказывает, когда подписант реально совершил действие.
Привязка сертификата и защита от путаницы ключей¶
Помимо sid и самой подписи в некоторых профилях применяется SigningCertificateV2 из RFC 5035, связывающий подпись с сертификатом через хэш и идентификаторы. Наличие такого атрибута не отменяет проверки сертификата; отсутствие не всегда делает общий CMS недействительным — это зависит от применяемого профиля.
Размеры подписи следует сверять с конкретной спецификацией и кодировкой. Для ГОСТ-2012/256 подпись в типичном бинарном представлении содержит две компоненты по 32 байта; для 512 — по 64 байта. Но разные API могут принимать или выдавать не raw-конкатенацию, а свои структуры. Нельзя без разбора заменять порядок r/s или направление байтов. См. форматы подписи, ГОСТ-2012/256, 512 и RFC 9215 для X.509-представлений.
Сложные случаи¶
Отдельно переданный документ: нужна точная побайтовая версия (detached); пересохранение PDF, замена CRLF и перекодировка MIME ломают хэш. Сертификат отсутствует: проверку можно выполнить с заранее полученным сертификатом, но его происхождение должно быть известно. Доверие не установлено: верная подпись не делает субъект доверенным. Сертификат отозван: поведение зависит от политики времени проверки и статуса CRL/OCSP; подробности в политике доверия.
При диагностике не используйте «отключить проверку сертификатов» как путь к успешному результату. Вместо этого сохраняйте независимые результаты: parse, signature, chain, revocation, policy. Практические проверки — OpenSSL CMS, GPGSM, сценарии совместимости.
Читайте первоисточники¶
- RFC 5652, раздел 5: SignedData.
- RFC 4490, разделы 2–3: хэши и подписи старого ГОСТ.
- Проект ТК26, разделы 4–5: Стрибог и подписи 2012.
- RFC 9215: представление ГОСТ Р 34.10-2012 в сертификатах.
Маршрут чтения: Проверка доверия → ASN.1 и OID → Диагностика