Перейти к содержимому
getnextpdf.com

Правильная проверка подписи

Spec: RFC 5280, §6Spec: RFC 6960Spec: RFC 5652

“Подпись действительна” обычно означает, что проверено только одно: математика сошлась. При правильной проверке контролируются как минимум пять независимых вещей, и любая из них может дать сбой так, что зелёная галочка потеряет смысл. На этой странице приведён полный набор проверок и объясняется, почему частичный ответ опасен.

Одно булево значение — самый опасный результат во всей этой теме. Оно подталкивает читателя воспринимать “действительна” как “заслуживает доверия”, хотя “действительна” может означать лишь, что байты не были изменены и подпись сошлась с ключом из сертификата, который истёк три года назад, был отозван в прошлом месяце и выстраивается в цепочку к удостоверяющему центру, который вы не признаёте. Каждое из этих условий — отдельная проверка. Программа, возвращающая одно булево значение, негласно решила, какие проверки важны, и сделала это за вас. В регулируемой или договорной среде довод “инструмент сказал, что действительна” не защитит, если инструмент проверил только самое дешёвое свойство.

Полная проверка отвечает на пять отдельных вопросов. Они независимы: успешное прохождение одной проверки ничего не говорит об остальных:

  1. Целостность — дают ли подписанные байты по-прежнему тот же хеш, который покрывает подпись? (Пересчитайте дайджест по диапазону байтов и сравните.)
  2. Подлинность — проверяется ли криптографическая подпись по открытому ключу из сертификата подписи над подписанными атрибутами?
  3. Путь сертификации — выстраивается ли этот сертификат в цепочку к якорю доверия, который выбрали вы, и действительно ли каждое звено?
  4. Время — был ли сертификат в пределах своего срока действия в соответствующий момент времени и является ли это время доверенным, а не заявленным самим подписантом?
  5. Отзыв — был ли сертификат не отозван на тот момент согласно данным (OCSP/CRL), которые вы действительно можете получить или которые встроены?

Вердикт “действительна”, при котором выполнены не все пять проверок, — это неполный ответ, который выглядит как полный.

Позиция NextPDF такова: каждый вопрос отдельный, и на каждый нужно ответить явно. Проверка никогда не сводится к одному оптимистичному флагу и не пропускается молча только потому, что оказалась неудобной. Это обеспечивается тестами. Поэтому страница отмечена как подкреплённая тестами, а не подкреплённая стандартом: поведение фиксирует тестовый набор, а не только пункт спецификации.

Целостность и подлинность тестируются сквозным образом. Известный сертификат подписывает реальную структуру подписанных атрибутов, а тестовый набор проверяет подпись соответствующим открытым ключом для нескольких временных векторов. Поэтому изменение, нарушающее каноническую структуру, ломает тест. Проверка пути сертификации закреплена тестами, которые намеренно искажают байт подписи и подтверждают, что результат не действителен, со структурированной причиной — не отброшенным исключением, а явно зафиксированным сбоем. Проверка токена метки времени разбита на отдельные шаги: декодирование, сведения о подписанте, подписанные атрибуты, дайджест сообщения, привязка сертификата, назначение ключа, подпись, момент создания. Каждый шаг тестируется отдельно, поэтому “метка времени проверена” означает, что проверен каждый шаг. Для метки времени подписи один из этих шагов не является ни необязательным, ни универсальным: оттиск сообщения внутри токена должен связывать ту подпись, которую он якобы покрывает, — хеш внутри токена должен совпадать с хешем собственного значения подписи подписанта, иначе токен удостоверяет другие байты. NextPDF предоставляет для этого специальную проверку verifySignatureTimestamp, которая принимает значение подписи как обязательный входной параметр, поэтому её нельзя пропустить, и безопасно завершается отказом при пустом входе, при нераспознанном или слабом (SHA-1) алгоритме оттиска, при некорректном оттиске или при несовпадении хеша. Мягкий сбой проверки отзыва (недоступный сервер ответов) отличается, в коде и в тестах, от однозначного “отозван”. Эти два случая никогда не смешиваются в один ответ.

  1. IntegrityRecompute the byte-range digest and compare it to the value the signature covers.
  2. AuthenticityVerify the cryptographic signature against the certificate’s public key, over the signed attributes — not the raw content.
  3. Certificate pathBuild and validate the chain to a trust anchor you chose; every link’s signature, validity, and constraints must hold.
  4. TimeConfirm the certificate was valid at the relevant instant, and that the instant is trusted time, not the signer’s clock.
  5. RevocationConfirm the certificate was not revoked at that time, using obtainable or embedded OCSP/CRL evidence.
Пять независимых вопросов, на которые по порядку отвечает правильная проверка подписи PDF. Каждый из них отдельный: успешное прохождение одного ничего не говорит об остальных, а пропуск любого делает общий результат неполным.

Здесь важен не вызов API, а вопросы, на которые нужно уметь ответить прежде, чем действовать на основании результата. Воспринимайте это как контрольный список, по которому вас будут спрашивать на ревью.

<?php
declare(strict_types=1);
// A correct validation produces a structured outcome, not one boolean.
// Before you trust a signature, you must be able to answer ALL of these:
//
// integrity : Does the byte-range digest still match? (tamper check)
// authenticity: Does the signature verify over the SIGNED ATTRIBUTES,
// not just the content?
// path : Does the certificate chain to a trust anchor YOU chose,
// with every link valid at the relevant time?
// time : Is the relevant time TRUSTED (a timestamp), or merely the
// signer's self-asserted clock?
// revocation : Was the certificate not revoked at that time, by evidence
// you obtained or that the document embedded?
//
// "valid: true" without an answer to every line above is an incomplete
// result. A path-validation outcome carries a `valid` flag AND a structured
// `reasons` list precisely so a failure says WHY — never a bare false.

Если по какой-либо строке ответ — “я не знаю”, честный статус — не “действительна”. Это “ещё не определено”, и считать эти два состояния одним и тем же — именно та ошибка, ради предотвращения которой существует эта страница.

Ловушка — приравнять “криптографически действительна” к “заслуживает доверия”. Целостность и подлинность вместе доказывают только то, что эти байты были подписаны владельцем этого ключа. Они ничего не говорят о том, был ли сертификат этого ключа доверенным, действующим или неотозванным. Документ, подписанный самостоятельно сгенерированным сертификатом, может быть “криптографически действителен” и при этом не стоить ничего. Обратная ловушка — трактовать неопределённый результат проверки отзыва (сервер ответов недоступен) как успех или как провал. Это ни то ни другое. Это неизвестно, и правильный валидатор сообщает об этом как о неизвестном, а не гадает в ту или иную сторону. Зелёная галочка, скрывающая, какие из пяти проверок действительно выполнялись, — это не результат проверки. Это решение, которое кто-то другой принял за вас.

NextPDF выполняет и тестирует структурные и криптографические проверки. Он не выбирает за вас якоря доверия и не гарантирует политику, надстроенную над ними. Каким сертификатам доверять — это решение уровня развёртывания, которое движок не может принять за вас. Цепочка, успешно выстроенная к якорю, которому не следовало доверять, всё равно остаётся проверкой, на которую нельзя полагаться. Данные об отзыве можно проверить, только если они доступны для получения или встроены. Недоступный сервер ответов даёт “неопределённо”, и превращение этого в вердикт — выбор политики, а не движка. Эта страница описывает набор проверок, а не юридическую достаточность. Имеет ли проверенная подпись то или иное юридическое значение, зависит от сертификата, подписанта, юрисдикции и обязательства. Как встроенные данные сохраняют возможность отвечать на эти вопросы с течением времени, рассказано в разделе Долгосрочная проверка; механизм диапазона байтов, лежащий в основе проверки целостности, описан в разделе Как подписи размещаются в PDF.

Доступность возможностей проверки по тарифным планам:

Набор проверок подписи — edition availability
EditionAvailability
Core

Целостность и подлинность над подписанными атрибутами, а также проверка пути сертификации по RFC 5280 §6 относительно предоставленного якоря доверия.

Pro

Добавляет проверку токена метки времени по RFC 3161: вопрос доверенного времени, разложенный на независимо проверяемые шаги.

Enterprise

Добавляет оценку отзыва (OCSP/CRL) и проверку относительно встроенного долгосрочного материала, при этом неопределённые результаты отличаются от однозначных.

  • Проверка целостности — пересчёт дайджеста по диапазону байтов и сравнение со значением, которое покрывает подпись.
  • Проверка подлинности — проверка криптографической подписи по открытому ключу сертификата подписи над подписанными атрибутами.
  • Подписанные атрибуты — аутентифицированные атрибуты CMS (content-type, message-digest, signing-time, signing-certificate-v2), над которыми и вычисляется подпись.
  • Проверка пути сертификации — построение и проверка цепочки от сертификата подписи до выбранного якоря доверия (RFC 5280 §6).
  • Якорь доверия — удостоверяющий центр, которому вы решили доверять; корень приемлемого пути.
  • Проверка отзыва — определение того, был ли сертификат отозван в соответствующий момент времени, через OCSP или CRL.
  • Неопределённо — результат проверки отзыва, который не является ни “действителен”, ни “отозван”, поскольку данные не удалось получить; не успех и не провал.
  • Привязка оттиска (метка времени подписи) — проверка того, что оттиск сообщения метки времени подписи равен хешу значения подписи SignerInfo подписанта, которое она покрывает, так что метка времени доказуемо относится именно к этой подписи, а не к другим байтам.