콘텐츠로 이동
getnextpdf.com

Enterprise 에디션

일괄 서명 검증

NextPDF Enterprise는 여러 PDF 문서의 디지털 서명을 한 번의 호출로 검증합니다. NextPDF\Enterprise\Signature\BatchSignatureValidator::validate()는 문서 목록을 받아 BatchValidationReport를 반환합니다. 모든 서명은 동일한 fail-closed 파이프라인을 거칩니다. 서명된 바이트 범위에 대한 암호학적 CMS 인증, 신뢰 앵커 인증서 체인 검증, OCSP/CRL 폐기 검사입니다. 이 보고서는 문서별 및 서명별 세부 정보 — CertChainStatus, RevocationStatus, TimestampStatus — 를 담고 있어 컴플라이언스 도구가 기록된 증거로부터 모든 판정을 다시 도출할 수 있습니다.

판정 모델은 의도적으로 엄격합니다. 서명은 모든 증거가 긍정적으로 확립되었을 때만 Valid입니다. 폐기 증거가 없으면 Valid가 아닌 Indeterminate가 됩니다. 이 페이지는 일괄 오케스트레이터와 그 결과 타입을 다룹니다. 단일 문서 AdES 검증 측면은 서명 검증에 문서화되어 있습니다. 장기 검증 자료 임베딩은 Archive에 문서화되어 있습니다.

이 기능은 NextPDF Enterprise (nextpdf/enterprise)에 포함되어 있으며 Enterprise 등급 라이선스 엔벨로프로 활성화됩니다. 해당 권한이 없는 배포는 이 기능의 클래스를 로드하지 않습니다. 에디션 비교 및 라이선스 받기.

Terminal window
composer require nextpdf/enterprise

nextpdf/premium 메타패키지도 Enterprise 패키지를 해석합니다. 활성화는 Enterprise 라이선스 엔벨로프를 사용합니다. 라이선스 및 활성화를 참조하세요. 일괄 타입은 NextPDF\Enterprise\Signature 아래에서 자동 로드됩니다. 엔진 기준선을 넘는 PHP 확장은 필요하지 않습니다.

validate() 한 번의 호출은 DocumentSignatureInput 값 목록을 처리합니다. 각 입력은 문서 식별자, 원시 PDF 바이트, 그리고 선택적 PEM 인코딩 신뢰 앵커를 담고 있습니다. 검증기는 각 문서의 서명 딕셔너리를 추출하고 서명마다 세 단계를 실행합니다.

1단계 — 암호학적 인증. /Contents에서 가져온 분리형 CMS/PKCS#7 블롭은 /ByteRange가 포괄하는 바이트에 대해 검증됩니다. 검증기는 콘텐츠 다이제스트를 직접 재계산하여 messageDigest signed attribute와 비교합니다. 생성자가 제공한 다이제스트를 절대 신뢰하지 않습니다(RFC 5652 §5.6). 서명 값이 검증되어야 하고, 서명 인증서가 CMS에 바인딩되어 있어야 합니다. /Contents 또는 /ByteRange가 없거나 잘못된 경우, 파싱할 수 없는 CMS, 다이제스트 불일치, 또는 서명 검사 실패는 모두 fail closed됩니다. SHA-1로 검증되는 서명은 약한 것으로 취급되며 결코 완전한 통과가 아닙니다.

2단계 — 체인 검증 및 신뢰 앵커링. CMS에서 복구된 서명자 체인은 예상 인증 경로로 검증됩니다. 여러분이 제공하는 trustedCerts는 RFC 5280 §6.1.1의 의미에서 신뢰 앵커 입력입니다. 체인 종단은 제공된 앵커와 DER SHA-256 지문으로 일치해야 합니다. 구조적으로 일관되지만 종단이 구성된 앵커가 아닌 체인은 결코 신뢰됨으로 보고되지 않습니다. 사용 가능한 앵커가 없으면 구조적 판정만 보고되며 CertChainStatus::$trustedfalse로 유지됩니다.

3단계 — 폐기. 폐기는 인증 후 복구된 체인에 대해 실행되며, 폐기 검사가 성공적인 경로 검증 뒤에 따르는 ETSI EN 319 102-1 모델(clause 5.2.6.2)을 반영합니다. OCSP가 기본입니다. 암호학적으로 검증된 응답만 Good 또는 Revoked로 계산됩니다. CRL 경로는 대체 수단이며 목록 신선도를 입증합니다. 두 클라이언트 모두 구성되지 않으면 상태는 unavailable입니다.

서명별 판정은 SignatureValidationStatus입니다. 이 분류 체계는 서명별 세분성에서 ETSI EN 319 102-1 상태 모델(TOTAL-PASSED / TOTAL-FAILED / INDETERMINATE)을 반영합니다:

증거판정
인증서가 폐기됨으로 확정됨Invalid (결정적, 다른 검사와 무관)
CMS 인증 실패, 서명자 자료를 복구하지 못함Error
CMS 인증 실패, 서명자 자료 존재Invalid
인증되었으나 체인이 검증되지 않음Invalid (체인이 없으면 Error)
인증 및 체인 유효, 그러나 확정된 신뢰 앵커 없음Indeterminate
인증, 체인 유효, 신뢰됨, 그러나 결정적 미폐기 증거 없음Indeterminate
위 모든 항목이 긍정적으로 확립됨Valid

결정적 미폐기 규칙. “폐기가 입증되지 않음”은 “미폐기가 입증됨”과 같지 않습니다. Valid 판정은 적어도 하나의 Good 폐기 결과를 요구합니다. 검증된 good OCSP 응답이 결정적 형태입니다. 이는 서명자 인증서 자체의 상태를 주장합니다. 암호학적으로 수용된 신선한 CRL도 이 구현에서 게이트를 충족하지만, 이는 신선도-및-무결성 입증으로서만 그러합니다 — 이 경로는 시리얼별 항목을 파싱하지 않으므로 시리얼별 폐기 보증을 제공하지 않으며 결코 긍정적 revoked 판정을 내지 않습니다. 긍정적 폐기 탐지가 중요한 곳이면 어디서든 OCSP를 구성하세요. CRL만 사용하는 배포는 폐기된 인증서를 Invalid로 드러내지 않습니다. OCSP와 CRL 결과가 모두 Unknown 또는 Unavailable일 때 폐기 상태는 미확정이며 판정은 Indeterminate입니다. 이는 ETSI EN 319 102-1을 따릅니다. 확보되지 않은 폐기 상태 정보는 통과가 아닌 INDETERMINATE로 귀결됩니다(clause 5.1.3, TRY_LATER). 이는 하위 호환성 영향이 있는 3.1.0의 동작 강화입니다. 이전 릴리스는 결정적 폐기 증거 없이도 Valid를 보고할 수 있었습니다. OCSP나 CRL 클라이언트를 구성하지 않는 배포는 이제 이전에 Valid를 보던 곳에서 흔히 Indeterminate를 보게 됩니다.

두 가지 경계가 이 기능을 정직하게 규정합니다. 첫째, 일괄 검증기는 임베딩된 타임스탬프 토큰을 평가하지 않습니다. 일괄 결과의 TimestampStatus는 항상 부재 상태입니다. RFC 3161 타임스탬프 평가는 단일 문서 검증 측면에 속합니다. 서명 검증을 참조하세요. 둘째, 이 페이지는 읽기 전용 검증입니다. 장기 유효성을 위한 DSS/VRI 자료 임베딩은 Archive 기능입니다.

핵심을 지탱하는 결정은 fail-closed 판정 생성기입니다. Valid는 세 가지 축 모두에 대한 긍정적 증거로부터만 발행됩니다. 암호학적 인증, 신뢰 앵커 체인, 그리고 결정적 미폐기입니다. 확립되지 않은 것은 통과로 기본 처리되는 대신 Indeterminate로 격하되며, 이것이 폐기 자료가 없을 때의 EN 319 102-1 자세입니다. 일괄 처리량이 엄격함을 되사지 않습니다. 일괄 계층은 단일 문서에 사용되는 것과 동일한 감사된 CMS 검증기 위의 오케스트레이션이므로, 1,000개 문서 실행은 동일한 암호를 적용합니다. 보고서는 또한 증거와 판정을 분리합니다 — CertChainStatusRevocationStatus는 각 판정이 근거로 삼는 입력을 기록하므로 감사자가 나중에 다시 도출할 수 있습니다.

설계 배경: 타협 없는 대규모 서명.

아래 모든 심볼은 nextpdf/enterprise 3.1.0의 공개 API입니다.

final class BatchSignatureValidator
{
public function __construct(
?SignatureExtractor $extractor = null,
?CertificateChainValidator $chainValidator = null,
private readonly ?OcspClient $ocspClient = null,
private readonly ?CrlFetcher $crlFetcher = null,
?CmsSignatureDataExtractor $cmsExtractor = null,
private readonly ClockInterface $clock = new SystemClock(),
)
public function validate(array $inputs): BatchValidationReport
}

예외 또는 실패 조건: 입력 목록이 비어 있으면 validate()\InvalidArgumentException을 던지고, 일괄이 1,000개 문서를 초과하면 \OverflowException을 던집니다. 파싱 가능한 PDF가 아닌 문서는 예외를 던지지 않고 문서별 Error 결과가 됩니다. $clock는 CRL 신선도 결정에 사용되는 PSR-20 Psr\Clock\ClockInterface이므로, 고정된 테스트 클록 하에서 판정은 결정적입니다.

final readonly class DocumentSignatureInput
{
public string $documentId;
public function __construct(
string $documentId,
public string $pdfData,
public array $trustedCerts = [],
)
}

예외 또는 실패 조건: $documentId가 빈 문자열이면 \InvalidArgumentException. $trustedCerts는 PEM 인코딩 신뢰 앵커 인증서 목록입니다.

final readonly class BatchValidationReport
{
public function __construct(
public array $documents,
public int $totalDocuments,
public int $totalSignatures,
public int $totalValid,
public int $totalInvalid,
public float $durationMs,
)
public function allValid(): bool
public function hasDocumentsWithoutSignatures(): bool
public function toJson(?CertPiiGuard $piiGuard = null): string
}

예외 또는 실패 조건: 인코딩이 실패하면 toJson()\JsonException을 던집니다. allValid()는 서명이 존재하고 그 중 어느 것도 non-valid가 아닐 때만 true입니다. 기본적으로 toJson()은 프라이버시 우선 NextPDF\Enterprise\Signature\Eidas\CertPiiGuard를 적용하며, 이는 서명자 이름, 루트 발급자, TSA 이름, 체인 이슈 진단을 마스킹합니다. 가드의 API는 eIDAS 보증 수준을 참조하세요.

DocumentValidationResultDocumentValidationStatus

섹션 제목: “DocumentValidationResult와 DocumentValidationStatus”
final readonly class DocumentValidationResult
{
public function __construct(
public string $documentId,
public DocumentValidationStatus $status,
public array $signatures,
public int $validCount,
public int $invalidCount,
)
public function hasSignatures(): bool
public function totalSignatures(): int
}
enum DocumentValidationStatus: string
{
case AllValid = 'all_valid';
case SomeInvalid = 'some_invalid';
case AllInvalid = 'all_invalid';
case NoSignatures = 'no_signatures';
case Error = 'error';
}

예외 또는 실패 조건: 없음. 불변 값 객체와 백드 enum입니다.

SignatureValidationResultSignatureValidationStatus

섹션 제목: “SignatureValidationResult와 SignatureValidationStatus”
final readonly class SignatureValidationResult
{
public function __construct(
public SignatureValidationStatus $status,
public CertChainStatus $certChain,
public TimestampStatus $timestamp,
public RevocationStatus $revocation,
public string $signer,
public string $level = '',
public string $subFilter = '',
public string $reason = '',
)
public function isValid(): bool
}
enum SignatureValidationStatus: string
{
case Valid = 'valid';
case Invalid = 'invalid';
case Indeterminate = 'indeterminate';
case Error = 'error';
}

예외 또는 실패 조건: 없음. $signer는 인증이 통과했을 때 CMS로 검증된 인증서 주체이고, 그렇지 않으면 빈 문자열입니다. $levelSubFilter에서 파생된 레이블(예: ETSI.CAdES.detached에 대해 B-B)이며, AdES 적합성 판정이 아닙니다.

final readonly class CertChainStatus
{
public function __construct(
public bool $valid,
public bool $trusted,
public int $chainLength,
public string $rootIssuer,
public array $issues = [],
)
public function hasIssues(): bool
}

예외 또는 실패 조건: 없음. $trusted는 확정된 신뢰 앵커 소속 적중 시에만 설정되며, 앵커 목록이 비어 있지 않다는 사실로부터는 결코 설정되지 않습니다.

final readonly class RevocationStatus
{
public function __construct(
public RevocationCheckResult $ocspStatus,
public RevocationCheckResult $crlStatus,
public bool $isRevoked,
public ?DateTimeImmutable $revocationDate = null,
)
public static function unavailable(): self
public function hasConclusiveGood(): bool
}
enum RevocationCheckResult: string
{
case Good = 'good';
case Revoked = 'revoked';
case Unknown = 'unknown';
case Unavailable = 'unavailable';
}

예외 또는 실패 조건: 표시된 멤버로부터는 없음. 이 클래스는 또한 증거 검증 정적 팩토리(good(), revoked(), fromResults())를 노출하며, 주장된 상태가 OCSP/CRL 증거와 모순될 때 \InvalidArgumentException을 던집니다 — 폐기된 결과는 결코 미폐기로 발행될 수 없으며 그 반대도 마찬가지입니다. hasConclusiveGood()는 적어도 하나의 검사가 Good인 미폐기 상태에 대해서만 true입니다.

final readonly class TimestampStatus
{
public function __construct(
public bool $present,
public bool $valid,
public ?DateTimeImmutable $timestampTime = null,
public string $tsaName = '',
public array $issues = [],
)
public static function absent(): self
}

예외 또는 실패 조건: 없음. 일괄 결과에서 이것은 항상 absent() 상태입니다. 예외 상황 및 주의점을 참조하세요.

문서 하나를 검증하고 보고서를 읽습니다. 이 예제는 서명되지 않은 PDF를 사용하므로 출력이 결정적입니다.

batch-quick-start.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Signature\BatchSignatureValidator;
use NextPDF\Enterprise\Signature\DocumentSignatureInput;
// A minimal, unsigned PDF: the validator reports it as no_signatures.
$unsigned = "%PDF-1.7\n1 0 obj\n<< /Type /Catalog >>\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF\n";
$validator = new BatchSignatureValidator();
try {
$report = $validator->validate([
new DocumentSignatureInput(documentId: 'doc-001', pdfData: $unsigned),
]);
} catch (\InvalidArgumentException $e) {
// Empty input list, or an empty documentId.
echo 'Rejected: ' . $e->getMessage() . "\n";
exit(1);
}
echo 'Documents: ' . $report->totalDocuments . "\n";
echo 'Signatures: ' . $report->totalSignatures . "\n";
foreach ($report->documents as $doc) {
echo $doc->documentId . ': ' . $doc->status->value . "\n";
}
echo 'All valid: ' . ($report->allValid() ? 'yes' : 'no') . "\n";
echo 'Unsigned documents: ' . ($report->hasDocumentsWithoutSignatures() ? 'yes' : 'no') . "\n";

예상 출력:

Documents: 1
Signatures: 0
doc-001: no_signatures
All valid: no
Unsigned documents: yes

여기서 allValid()no를 보고하는 점에 유의하세요. 이는 적어도 하나의 서명과 non-valid 결과가 없음을 요구하므로, 빈 서명 집합은 결코 조용히 통과하지 않습니다.

폐기 클라이언트, 신뢰 앵커, 일괄 청킹, PII 보호 JSON 보고서와 함께 서명된 계약서 디렉터리를 검증합니다.

batch-validate-contracts.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Security\Ltv\CrlFetcher;
use NextPDF\Enterprise\Security\Ltv\OcspClient;
use NextPDF\Enterprise\Security\Ltv\OcspResponseCache;
use NextPDF\Enterprise\Signature\BatchSignatureValidator;
use NextPDF\Enterprise\Signature\DocumentSignatureInput;
use NextPDF\Enterprise\Signature\SignatureValidationStatus;
// Any PSR-18 client works; Guzzle shown here.
$httpClient = new \GuzzleHttp\Client(['timeout' => 10]);
// Revocation clients make a conclusive non-revoked (Good) result reachable.
// Without them, every verdict tops out at Indeterminate. The response cache
// lets repeat signers across the batch resolve without extra network calls.
$validator = new BatchSignatureValidator(
ocspClient: new OcspClient($httpClient, cache: new OcspResponseCache()),
crlFetcher: new CrlFetcher($httpClient),
);
// Trust anchors are an input: the chain terminus must match one of these.
$anchors = [(string) file_get_contents('/etc/nextpdf/trust/enterprise-root.pem')];
$inputs = [];
foreach (glob('/var/contracts/signed/*.pdf') ?: [] as $path) {
$inputs[] = new DocumentSignatureInput(
documentId: basename($path),
pdfData: (string) file_get_contents($path),
trustedCerts: $anchors,
);
}
$exit = 0;
// One call is capped at 1,000 documents; chunk larger runs.
foreach (array_chunk($inputs, 1000) as $batch) {
try {
$report = $validator->validate($batch);
// Signer PII is redacted by default in the serialized report.
file_put_contents('/var/log/nextpdf/batch-report.jsonl', $report->toJson() . PHP_EOL, FILE_APPEND); // one JSON document per line
} catch (\InvalidArgumentException | \OverflowException $e) {
fwrite(STDERR, 'Batch rejected: ' . $e->getMessage() . "\n");
exit(2);
} catch (\JsonException $e) {
fwrite(STDERR, 'Report encoding failed: ' . $e->getMessage() . "\n");
exit(3);
}
foreach ($report->documents as $doc) {
foreach ($doc->signatures as $sig) {
if ($sig->status !== SignatureValidationStatus::Valid) {
$exit = 1;
fwrite(STDERR, sprintf(
"%s: %s (chain trusted: %s, revoked: %s)\n",
$doc->documentId,
$sig->status->value,
$sig->certChain->trusted ? 'yes' : 'no',
$sig->revocation->isRevoked ? 'yes' : 'no',
));
}
}
}
}
exit($exit);

예상 출력(stderr, 폐기 증거가 확보되지 않은 문서 하나에 대해. 다른 줄은 입력에 따라 달라집니다):

contract-0042.pdf: indeterminate (chain trusted: yes, revoked: no)

JSON 보고서는 서명자 신원 필드를 기본 CertPiiGuard를 통해 직렬화하므로, 서명별 항목은 다음과 같이 보입니다(발췌, 예시):

{
"status": "indeterminate",
"signer": "[REDACTED]",
"level": "B-B",
"subFilter": "ETSI.CAdES.detached"
}
  • 빈 입력 목록은 \InvalidArgumentException을 던집니다. 한 번의 호출에 1,000개를 초과하는 문서는 \OverflowException을 던집니다. 프로덕션 예제처럼 더 큰 실행은 청크로 나누세요.
  • 이전 릴리스에서 업그레이드: OCSP나 CRL 클라이언트가 구성되지 않으면 폐기는 unavailable이므로 어떤 서명도 Valid에 도달할 수 없습니다. 이전 릴리스는 여기서 Valid를 보고했지만, 3.1.0은 Indeterminate를 보고합니다(개념 개요 참조).
  • 문서 수준 카운터는 엄격합니다. ValidvalidCount를 증가시킵니다. Invalid, Indeterminate, Error는 모두 invalidCount를 증가시킵니다. 따라서 유일한 서명이 Indeterminate인 문서는 all_invalid를 보고합니다. 그 구분이 중요하면 서명별 status로 게이트하세요.
  • OCSP 검사는 쿼리에 발급자가 필요하기 때문에 복구된 체인에 적어도 두 개의 인증서가 있을 때만 실행됩니다. 단일 인증서 체인은 CRL 경로 또는 unavailable로 넘어갑니다.
  • crlStatus는 일괄 결과에서 결코 revoked를 보고하지 않습니다. CRL 대체는 목록 신선도만 입증합니다. 권위 있는 폐기 결과는 OCSP에서 나옵니다.
  • timestamp는 일괄 결과에서 항상 absent()입니다. 일괄 검증기는 임베딩된 RFC 3161 토큰을 평가하지 않습니다. 타임스탬프 평가에는 서명 검증을 사용하세요.
  • signer는 인증이 실패했을 때 비어 있습니다. 설정된 경우, 이는 CMS로 검증된 인증서의 주체 CN(또는 O)이며 — 서명 딕셔너리의 인증되지 않은 /Name 문자열이 결코 아닙니다.
  • trustedCerts 항목은 PEM 인증서여야 합니다. 비어 있거나 잘못된 앵커 목록은 trusted: false인 구조 전용 체인 판정을 낳으며, 판정을 Indeterminate로 제한합니다.
  • PDF 헤더로 시작하지 않는 바이트는 서명이 0개인 문서별 error 상태를 생성합니다 — 예외는 없습니다.
  • toJson()은 기본적으로 PII를 삭제합니다. 서명자 신원을 처리할 문서화된 적법 근거를 보유한 경우에만 new CertPiiGuard(disclosePii: true)를 전달하세요.
  • Fail-closed 판정 생성기. Valid는 다음을 모두 요구합니다. /ByteRange 다이제스트에 대한 검증된 CMS 인증, 유효한 체인, 확정된 신뢰 앵커 소속, 그리고 결정적 미폐기 상태입니다. 확립되지 않은 모든 검사는 판정을 격하시킵니다. 어떤 것도 통과로 기본 처리되지 않습니다.
  • 신원 세탁 없음. 보고되는 서명자는 암호학적으로 바인딩된 인증서 주체입니다. /Name 항목은 공격자가 제어하는 메타데이터이며 결코 서명자로 드러나지 않습니다.
  • 약한 알고리즘은 결코 통과하지 않습니다. 검증되는 SHA-1 서명도 여전히 non-valid로 보고됩니다. 약한 다이제스트 하의 암호학적 유효성은 완전한 통과로 세탁되지 않습니다.
  • 신뢰는 입력이지 추론이 아닙니다. 여러분이 제공하는 앵커는 DER SHA-256 지문으로 체인 종단과 대조됩니다(RFC 5280 §6.1.1). 체인의 자기 일관성이나 비어 있지 않은 앵커 목록만으로는 결코 신뢰를 확립하지 못합니다.
  • 폐기는 결정적입니다. 검증된 폐기 진술은 다른 모든 검사와 무관하게 Invalid를 강제합니다. 확보되지 않은 증거는 Indeterminate를 강제합니다.
  • 직렬화 출력에서 기본 프라이버시. toJson()은 명시적으로 해제하지 않는 한 서명자 CN, 루트 발급자 DN, TSA 이름, 체인 이슈 진단을 마스킹하여, 직렬화 경계에서 GDPR Article 5(1)(c) 데이터 최소화를 구현합니다.
  • 결정적 시간. CRL 신선도 결정은 호스트 벽시계가 아닌 주입된 PSR-20 클록을 읽으므로, 폐기 판정은 테스트에서 재현 가능합니다.

NextPDF Enterprise는 ETSI EN 319 102-1(3값 검증 상태 모델과 확보되지 않은 폐기 정보가 INDETERMINATE로 귀결된다는 규칙), RFC 5652 §5.6(검증자 측 다이제스트 재계산), RFC 5280 §6.1(경로 검증에 대한 신뢰 당사자 입력으로서의 신뢰 앵커)로부터 영향을 받은 동작을 구현합니다. 지원은 적합성이 아니며, 적합성은 인증이 아닙니다. NextPDF는 어떤 인증도 보유하지 않으며 어떤 것도 부여하지 않습니다. 일괄 검증기는 인정된 검증 서비스가 아니며, 그 상태는 EN 319 102-1 분류 체계에 정렬된 엔지니어링 판정입니다 — 전체 clause 5 검증 프로세스의 TOTAL-PASSED/TOTAL-FAILED/INDETERMINATE 표시가 아닙니다. 특히 일괄 모드는 존재 증명이나 타임스탬프 처리를 수행하지 않습니다. 단일 문서 검증 측면이 그 영역을 다룹니다.

일괄 검증기는 어떤 FIPS 모드 정책도 참조하지 않으며, FIPS 모드를 활성화해도 일괄 판정이 바뀌지 않습니다. 그 검증 측 알고리즘 처리는 고정되어 있고 fail-closed입니다. 약한(SHA-1) 서명은 FIPS 모드 유무와 관계없이 결코 Valid로 보고되지 않습니다. Enterprise FIPS 모드 정책은 서명/생성 측면을 게이트하며, FIPS 140 — Deep Reference에 문서화되어 있습니다. FIPS 140 지원은 기능 진술이며, 검증이나 인증 주장이 아닙니다.

  • validate()는 빈 목록에 대해 \InvalidArgumentException을, 1,000개 문서를 초과하면 \OverflowException을 던집니다. 잘못된 문서는 결코 예외를 던지지 않고 문서별 error 결과를 생성합니다.
  • Valid는 다음의 결합을 요구합니다. CMS가 암호학적으로 검증됨, 체인 유효, 신뢰 앵커 소속 확정, 그리고 RevocationStatus::hasConclusiveGood()가 true.
  • 확정 폐기된 인증서는 결정적입니다. 다른 모든 증거와 무관하게 판정은 Invalid입니다.
  • 두 폐기 검사가 모두 Unknown/Unavailable이면 Valid가 아닌 Indeterminate를 의미합니다(3.1.0 강화, 하위 호환성 영향).
  • 확정된 신뢰 앵커가 없는, 인증되고 체인이 유효한 서명은 Indeterminate입니다 — 진정하지만 신뢰는 미확립입니다.
  • signer는 CMS로 검증된 주체이거나 빈 문자열입니다. /Name 항목은 결코 사용되지 않습니다.
  • timestamp는 일괄 결과에서 항상 부재 상태입니다.
  • validCountValid만 셉니다. 다른 모든 상태는 invalidCount로 계산되며, 문서 상태는 그 카운터로부터 집계됩니다.
  • toJson()은 가드가 명시적으로 전달되지 않는 한 프라이버시 우선 CertPiiGuard를 적용합니다.
  • 보고서 합계는 문서별 결과에 대한 정확한 합입니다. durationMs는 일괄에 대해 측정된 벽시계 시간입니다.

NextPDF Core의 Security / Signing 모듈은 생성자 측면입니다. CMS 서명을 만들고, RFC 3161 타임스탬프를 적용하며, 서명 시점에 임베딩하는 자료에 대해 체인과 폐기를 검증합니다. Core는 검증 측 일괄 오케스트레이터를 제공하지 않습니다. 다중 문서 보고서도, 집계 상태 분류 체계도, 제3자 문서에 대한 OCSP/CRL 폐기 판정도, PII 보호 보고서 직렬화도 없습니다. Core만으로는 각 서명을 직접 추출하고 검증하며 자체 보고 체계를 구축해야 합니다. Enterprise 단일 문서 검증 측면(서명 검증)과 이 일괄 오케스트레이터가 그 계층을 제공합니다.

이 페이지는 외부에서 관찰 가능한 동작과 지원되는 공개 API 표면만 문서화합니다. 내부 네임스페이스 경로, 헬퍼 클래스, 메커니즘 표, 런북 파일명, 티켓 접두사는 범위 밖입니다.