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

Проверка наличия подписи и понимание границы доверия

С помощью инспектора Core определите, есть ли в PDF подпись, и чётко учитывайте границу доверия. Обнаружение — ещё не проверка. Криптографическую проверку, проверку пути доверия и проверку отзыва выполняют premium-редакции или внешний верификатор.

  • Установлен Core: composer require nextpdf/core:^3.
  • PDF-файл для проверки.
  1. Прочитайте байты PDF-файла.
  2. Создайте Inspector и вызовите inspect().
  3. Проверьте InspectResult::$hasSigned. Значение true означает, что файл содержит словарь подписи.
  4. Проверьте InspectResult::$isEncrypted и флаги риска, чтобы получить дополнительный контекст.
  5. Передайте файл верификатору, чтобы получить криптографическое решение. Инспектор сообщает о наличии, а не о действительности.

На схеме ниже показана граница: наличие — это не действительность. Там же показаны проверки, которые всё равно должен выполнить настоящий верификатор.

false

true

Yes

No

Yes

No / unknown

Incoming PDF

Inspector.inspect — Quick

hasSigned?

No signature present

Signature dictionary PRESENT

NOT yet validity

Recompute byte-range digest

ISO 32000-2 §12.8.1

Validate CMS SignedData

Build & check X.509 path

to a chosen trust anchor

Check revocation OCSP / CRL

Long-term input?

Read DSS validation material

All checks evaluated

Every check passed?

Trustworthy

Not trusted — do not act

Diagram
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Inspect\Inspector;
use NextPDF\Inspect\InspectConfig;
$pdfData = file_get_contents(__DIR__ . '/incoming.pdf');
if ($pdfData === false || $pdfData === '') {
fwrite(STDERR, "Cannot read incoming.pdf\n");
exit(1);
}
$inspector = new Inspector();
// InspectConfig::quick() selects InspectDepth::Quick. This is the only
// depth that runs offline: a default Inspector has no Spectrum sidecar,
// and without a sidecar Quick is the sole path that returns a result.
// Standard and Full require the sidecar (see the SIDECAR-001 edge case).
$result = $inspector->inspect(
$pdfData,
InspectConfig::quick(),
);
// hasSigned reports the PRESENCE of a signature dictionary.
// It does NOT mean the signature verifies.
if ($result->hasSigned) {
echo "A signature is present in incoming.pdf\n";
echo "Encrypted: " . ($result->isEncrypted ? 'yes' : 'no') . "\n";
echo "Next step: run a cryptographic verifier before trusting it.\n";
} else {
echo "No signature found in incoming.pdf\n";
}

Для подписанного входного файла:

A signature is present in incoming.pdf
Encrypted: no
Next step: run a cryptographic verifier before trusting it.

Для неподписанного входного файла:

No signature found in incoming.pdf
  • Наличие — это не действительность: граница. InspectResult::$hasSigned сообщает, что в файле есть словарь подписи. Он не проверяет структуру Cryptographic Message Syntax (CMS), дайджест по диапазону байтов, сертификат подписанта, цепочку сертификатов или статус отзыва. Изменённый файл всё равно может сообщить hasSigned = true. Никогда не считайте наличие подтверждением целостности или авторства.
  • Что требуется для полной проверки. Полная проверка пересчитывает дайджест по диапазону байтов и сравнивает его (ISO 32000-2 §12.8.1), проверяет CMS SignedData, строит и проверяет путь X.509 до доверенного якоря и проверяет отзыв через Online Certificate Status Protocol (OCSP) или список отозванных сертификатов (CRL). Для долгосрочных входных файлов данные проверки хранятся в Document Security Store (DSS) (ETSI EN 319 142-2 §6.3.1). Эти операции выполняются через контракты SignerInterface и LtvManagerInterface; рабочие реализации поставляются в редакциях Pro и Enterprise. Внешний валидатор — ещё один поддерживаемый путь.
  • Глубина проверки и sidecar (SIDECAR-001). В new Inspector() по умолчанию Spectrum sidecar не настроен. Без sidecar результат возвращает только InspectDepth::Quick. InspectDepth::Quick использует внутрипроцессный резервный механизм PHP. Для InspectDepth::Standard и InspectDepth::Full требуется sidecar. Если sidecar недоступен, они выбрасывают InspectException с кодом INSPECT-SIDECAR-001 (“Spectrum sidecar is required for Standard/Full depth inspection”, retryable = true). Для конструктора InspectConfig глубина по умолчанию — Standard. Поэтому простой new InspectConfig() (или new InspectConfig(depth: InspectDepth::Standard)) не подходит для офлайн-использования. Он выбрасывает SIDECAR-001 ещё до того, как hasSigned будет прочитан. Используйте InspectConfig::quick() для офлайн-обнаружения наличия, как в этом рецепте. Ни одна глубина не выполняет криптографическую проверку подписи в Core.
  • Пустой ввод. Для пустой строки выбрасывается InspectException с кодом INSPECT-INPUT-001. Защитите чтение файла проверкой.
  • Несколько подписей. Флаг наличия не подсчитывает подписи и не отличает подпись утверждения от метки времени документа. Используйте специализированный верификатор, когда важны количество подписей или вердикт по каждой из них.
УтверждениеСпецификацияПунктидентификатор (reference_id)
Значение подписи хранится в записи Contents словаря подписи.ISO 32000-2§12.8.1
Проверка пересчитывает дайджест по ByteRange, исключая значение подписи.ISO 32000-2§12.8.1
Данные долгосрочной проверки хранятся в DSS.ETSI EN 319 142-2§6.3.1

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