콘텐츠로 이동

기존 서명 검사와 신뢰 경계 이해하기

이 레시피는 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는 파일에 서명 딕셔너리가 있음을 보고합니다. 이 값은 CMS 구조, 바이트 범위 다이제스트, 서명 인증서, 인증서 체인, 폐기 상태를 확인하지 않습니다. 변조된 파일도 여전히 hasSigned = true로 보고될 수 있습니다. 존재 여부를 무결성이나 작성자 증명의 근거로 절대 간주하지 마십시오.
  • 전체 검증에 필요한 것. 완전한 판정을 내리려면 바이트 범위 다이제스트를 다시 계산해 비교하고(ISO 32000-2 §12.8.1), CMS SignedData를 검증하며, 신뢰 앵커까지의 X.509 경로를 구축해 확인하고, OCSP 또는 CRL을 통해 폐기 상태를 확인해야 합니다. 장기 입력의 경우 검증 데이터는 DSS에 저장됩니다(ETSI EN 319 142-2 §6.3.1). 이러한 작업은 SignerInterfaceLtvManagerInterface 계약 뒤에서 수행됩니다. 프로덕션 구현은 Pro 및 Enterprise 에디션에 포함되어 있습니다. 외부 검증기도 지원되는 다른 경로입니다.
  • 검사 깊이와 사이드카(SIDECAR-001). 기본 new Inspector()에는 Spectrum 사이드카가 구성되어 있지 않습니다. 사이드카가 없으면 InspectDepth::Quick만 결과를 반환합니다. InspectDepth::Quick은 인프로세스 PHP 대체 경로를 사용합니다. InspectDepth::StandardInspectDepth::Full은 모두 사이드카가 필요합니다. 사용 가능한 사이드카가 없으면 INSPECT-SIDECAR-001 코드와 함께 InspectException을 발생시킵니다(“Spectrum sidecar is required for Standard/Full depth inspection”, retryable = true). InspectConfig 생성자의 기본 깊이는 Standard입니다. 따라서 단순한 new InspectConfig()(또는 new InspectConfig(depth: InspectDepth::Standard))는 오프라인에서 사용할 수 없습니다. 이는 hasSigned를 읽기도 전에 SIDECAR-001을 발생시킵니다. 이 레시피처럼 오프라인 존재 감지에는 InspectConfig::quick()를 사용하십시오. Core에서는 어떤 깊이도 암호화 서명 검증을 수행하지 않습니다.
  • 빈 입력. 빈 문자열은 INSPECT-INPUT-001 코드와 함께 InspectException을 발생시킵니다. 읽기 결과를 확인하십시오.
  • 다중 서명. 존재 플래그는 서명 수를 세지 않으며, 승인 서명과 문서 타임스탬프를 구분하지도 않습니다. 개수나 서명별 판정이 중요한 경우 전용 검증기를 사용하십시오.
진술사양reference_id (참조 ID)
서명 값은 서명 딕셔너리 Contents 항목에 저장됩니다.ISO 32000-2§12.8.1
검증에서는 ByteRange에 대해 다이제스트를 다시 계산하며, 서명 값은 제외합니다.ISO 32000-2§12.8.1
장기 검증 데이터는 DSS에 저장됩니다.ETSI EN 319 142-2§6.3.1

이 레시피는 서명을 감지합니다. 어떤 서명이 유효하거나, 신뢰할 수 있거나, 폐기되지 않았다고 주장하지는 않습니다. 그 판정은 암호화 검증기가 내립니다.