Enterprise 에디션
신뢰 목록 — 심층 참조
한눈에 보기
섹션 제목: “한눈에 보기”이 페이지는 NextPDF Enterprise의 신뢰 목록 표면에 대한 심층 레퍼런스입니다. 이 표면은 NextPDF\Enterprise\Security\Tsl 네임스페이스의 열두 개 공개 클래스입니다. NextPDF\Enterprise\Security\Tsl\TslPolicyEnforcer는 오케스트레이션된 진입점입니다. 이것은 HTTP fetch, XMLDSig 검증, 구조적 파싱, 그리고 nextUpdate 만료 게이트가 모두 통과할 때만 TslDocument를 반환합니다. 그런 다음 TslTrustAnchorProvider::buildBundle()는 활성 CA/QC 서비스로부터 신뢰 앵커 번들을 도출하며, 어떤 앵커도 추출되기 전에 호출자가 제공한 시점에 최신성을 다시 확인합니다. 모든 실패는 타입이 지정된 예외를 발생시킵니다. 어떤 단계도 조용히 저하되지 않습니다. 이 파이프라인은 호출자가 제공한 경우 EU 회원국 신뢰 목록과 LOTL(List of Trusted Lists) 출처의 신뢰 앵커 검증을 지원합니다. 자동 LOTL 발견, 폴링, 피벗 처리는 범위 밖입니다.
가용성 및 라이선싱
섹션 제목: “가용성 및 라이선싱”이 기능은 NextPDF Enterprise(nextpdf/enterprise)에 포함되어 제공되며 Enterprise 등급 라이선스 봉투로 활성화됩니다. 해당 권한이 없는 배포에서는 이 기능의 클래스가 로드되지 않습니다. 에디션을 비교하고 라이선스를 받으세요.
공개 API 표면
섹션 제목: “공개 API 표면”| 심볼 | 매개변수 | 기본 동작 | 반환값 | 던지거나 실패하는 경우 | 참고 |
|---|---|---|---|---|---|
TslPolicyEnforcer | TslFetcher $fetcher, TslSignatureVerifier $verifier, TslXmlParser $parser | fetch, 서명 검증, 파싱, 만료 게이트를 하나의 진입점으로 결합 | — | 아래의 파이프라인 예외를 전파 | final; 구조상 fail-closed |
TslPolicyEnforcer::fetchAndVerify | string $url | 하나의 TSL을 fetch한 다음 그 바이트에 대해 verifyXml()을 실행 | TslDocument | TslFetchException, NetworkPolicyViolation, TslSignatureException, TslParseException | 네 단계가 모두 통과할 때만 반환 |
TslPolicyEnforcer::verifyXml | string $xml | 서명을 검증하고, 파싱하며, 오래된 목록을 거부 | TslDocument | TslSignatureException, TslParseException | 만료 여부는 현재 시스템 시각을 기준으로 평가 |
TslFetcher | ClientInterface $httpClient, RequestFactoryInterface $requestFactory, ?CacheInterface $cache = null, int $defaultTtlSeconds = 3600, int $maxBytes = 16_777_216, NetworkPolicy $networkPolicy = NetworkPolicy::ONLINE | ETag 기반 캐싱을 갖춘 HTTPS 전용 TSL/LOTL 조회 | — | — | final; SSRF 가드가 DNS 리바인딩 완화와 함께 사설, 루프백, 링크 로컬, 메타데이터 호스트를 차단 |
TslFetcher::fetch | string $url | If-None-Match 재검증을 통한 GET; 구성된 TTL 하에 본문과 ETag를 캐시 | string (원시 XML 바이트) | TslFetchException, NetworkPolicyViolation | 최대 $maxBytes 바이트를 읽음; STRICT_OFFLINE 하에서는 캐시된 본문만 제공 |
TslSignatureVerifier | array $trustAnchorsPem, int $clockTolerance = 0 | 구성된 신뢰 앵커에 고정된 XMLDSig 검증기 | — | 앵커 목록이 비어 있을 때 InvalidArgumentException | final; ALLOWED_SIG_ALG 및 ALLOWED_DIGEST_ALG의 허용 목록 |
TslSignatureVerifier::verify | string $xml | enveloped XMLDSig 서명을 fail-closed로 검증 | string (서명자 인증서 PEM) | 기계 판독 가능한 사유 코드를 갖춘 TslSignatureException | KeyInfo 인증서는 그 자체로 결코 신뢰되지 않음; 서명자는 구성된 앵커로 체인이 연결되어야 함 |
TslXmlParser::parse | string $xml | TslDocument로의 구조적 파싱; 서명과 무관 | TslDocument | TslParseException | 파싱 전에 모든 DOCTYPE를 fail-closed로 거부; LIBXML_NONET으로 로드; 호출자는 결과를 신뢰하기 전에 검증해야 함 |
TslTrustAnchorProvider::buildBundle | TslDocument $tsl, DateTimeImmutable $now | 먼저 최신성을 확인한 다음 활성 CA/QC 서비스의 인증서를 수집 | EnterpriseCaTrustAnchorBundle | TslParseException | 최신성 게이트가 모든 앵커 추출에 선행; 결과 집합이 비면 예외 발생 |
TslDocument | 여덟 개의 승격된 readonly 프로퍼티 (생성자 펜스 참조) | 불변의 파싱된 TSL 값 객체 | — | — | final readonly; 소스에 @api 주석 |
TslDocument::isStale | DateTimeImmutable $now | fail-closed UTC 파싱 후 nextUpdate를 $now와 비교 | bool | TslParseException | 명시적인 Z 또는 숫자 오프셋 지정자를 요구 |
TslDocument::assertFresh | DateTimeImmutable $now | 목록이 오래되었거나 nextUpdate를 파싱할 수 없을 때 예외를 던짐 | void | TslParseException | 소비자 경계의 최신성 게이트 |
TslDocument::servicesOfType | string $serviceTypeIdentifier | ETSI 서비스 타입 URI로 서비스를 필터링 | list<TspService> | 던지지 않음 | — |
TslDocument::activeServices | — | granted 상태의 서비스만 반환 | list<TspService> | 던지지 않음 | Granted는 TspService::STATUS_GRANTED를 의미 |
TspService | 여덟 개의 승격된 readonly 프로퍼티 | TSL 내부의 하나의 신뢰 서비스 항목 | — | — | final readonly; 상태 및 서비스 타입 URI를 위한 상수 |
TspService::isGranted | — | granted URI에 대한 상태 동등성 | bool | 던지지 않음 | — |
TspService::isQualifiedCa | — | CA/QC URI에 대한 타입 동등성 | bool | 던지지 않음 | — |
TspServiceQualifier | string $qualifierUri, string $criteriaListAssert = 'all', array $policyOidConditions = [], array $keyUsageConditions = [] | 선택적 기준을 갖춘 하나의 ETSI 서비스 한정자 | — | — | final readonly; 상수 FOR_ESIG, FOR_ESEAL, FOR_WSA, QSCD_STATEMENT, NO_QSCD |
EnterpriseCaTrustAnchorBundle | array $anchorsPem, string $bundleVersion, string $bundleSha256 | 고정 앵커 번들; 생성 시점에 제공된 다이제스트를 제공된 앵커에 대해 검증 | — | InvalidArgumentException | buildBundle()에서 획득; 직접 생성하지 말 것; TrustAnchorStoreInterface를 구현 |
EnterpriseCaTrustAnchorBundle::containsFingerprint | string $anchorDerSha256Hex | DER 본문에 대한 16진수 SHA-256으로 앵커 멤버십 확인 | bool | 던지지 않음 | — |
EnterpriseCaTrustAnchorBundle::computeBundleSha256 | array $anchorsPem | 개행 정규화된 PEM 연결에 대한 정규 SHA-256 | string | 던지지 않음 | static |
TslFetchException | — | TSL 조회 실패를 알림 | — | — | final; RuntimeException을 확장 |
TslParseException | — | 구조적 또는 최신성 실패를 알림 | — | — | final; RuntimeException을 확장 |
TslSignatureException | string $reason, string $message | 사유 코드와 함께 XMLDSig 검증 실패를 알림 | — | — | final; 공개 readonly $reason (아래 사유 코드 참조) |
TslPolicyEnforcer
public function fetchAndVerify(string $url): TslDocumentpublic function verifyXml(string $xml): TslDocumentTslFetcher
public function __construct( private readonly ClientInterface $httpClient, private readonly RequestFactoryInterface $requestFactory, private readonly ?CacheInterface $cache = null, private readonly int $defaultTtlSeconds = 3600, private readonly int $maxBytes = 16_777_216, private readonly NetworkPolicy $networkPolicy = NetworkPolicy::ONLINE,) {}
public function fetch(string $url): stringTslSignatureVerifier
public function __construct(private readonly array $trustAnchorsPem, private readonly int $clockTolerance = 0)
public function verify(string $xml): stringTslXmlParser
public function parse(string $xml): TslDocumentTslTrustAnchorProvider
public function buildBundle(TslDocument $tsl, DateTimeImmutable $now): EnterpriseCaTrustAnchorBundleTslDocument
public function __construct( public string $schemeTerritory, public string $schemeOperatorName, public string $tslType, public int $sequenceNumber, public string $issueDateTime, public string $nextUpdate, public array $tspServices, public string $rawXmlSha256,) {}
public function isStale(DateTimeImmutable $now): boolpublic function assertFresh(DateTimeImmutable $now): voidpublic function servicesOfType(string $serviceTypeIdentifier): arraypublic function activeServices(): arrayTspService
public function __construct(public string $tspName, public string $serviceName, public string $serviceTypeIdentifier, public string $serviceStatus, public string $statusStartingTime, public string $serviceCertificatePem, public array $qualifiers, public array $additionalServiceInformation) {}
public function isGranted(): boolpublic function isQualifiedCa(): boolTspServiceQualifier
public function __construct(public string $qualifierUri, public string $criteriaListAssert = 'all', public array $policyOidConditions = [], public array $keyUsageConditions = []) {}EnterpriseCaTrustAnchorBundle
public function __construct(public array $anchorsPem, public string $bundleVersion, public string $bundleSha256)
public function containsFingerprint(string $anchorDerSha256Hex): boolpublic static function computeBundleSha256(array $anchorsPem): stringTslSignatureException
public function __construct(public readonly string $reason, string $message)TslSignatureException 사유 코드: missing_signature, untrusted_signer, invalid_signature, digest_mismatch, unsupported_algorithm, unsupported_transform, expired_anchor.
동작 계약
섹션 제목: “동작 계약”- 파이프라인 순서는 고정되어 있습니다: fetch, XMLDSig 검증, 구조적 파싱, 만료 게이트.
TslPolicyEnforcer는 네 단계가 모두 성공할 때만TslDocument를 반환합니다. 신뢰 목록은 신뢰 당사자가 진정성과 무결성을 확인할 수 있도록 스킴 운영자가 서명합니다 — ETSI TS 119 612 §5.7.1. TslXmlParser는 설계상 서명과 무관합니다. 호출자는 파싱된 어떤 필드를 신뢰하기 전에 서명을 검증해야 합니다.TslPolicyEnforcer::verifyXml()이 그 순서를 강제합니다.- 최신성 불변식은 모든 소비자 경계에서 강제됩니다.
nextUpdate가 지난 목록은 만료된 것이며 거부됩니다 — ETSI TS 119 612 §5.3.15.verifyXml()은 현재 시스템 시각을 기준으로 게이트하며,TslDocument::assertFresh()와buildBundle()은 호출자가 제공한 시점을 기준으로 게이트합니다. - 최신성 파싱은 fail-closed입니다. 날짜-시각 필드는 명시적 지정자를 갖춘 ISO 8601 UTC 값입니다 — ETSI TS 119 612 §5.1.3. 명시적인
Z또는 숫자 오프셋이 없는nextUpdate는TslParseException을 발생시킵니다. 그 값은 결코 서버의 로컬 타임존으로 재해석되지 않습니다. buildBundle()은 어떤 앵커를 추출하기 전에assertFresh($now)를 호출한 다음, granted이면서 CA/QC인 서비스만 허용합니다. Granted와 withdrawn은 적격 서비스 상태 URI입니다 — ETSI TS 119 612 §5.5.4. CA/QC는 적격 CA 서비스 타입 URI입니다 — ETSI TS 119 612 §5.5.1.1.- 번들 버전은 스킴 영역과 TSL 시퀀스 번호에서 도출됩니다. 시퀀스 번호는 릴리스 전반에 걸쳐 단조 증가합니다 — ETSI TS 119 612 §5.3.2. 번들 다이제스트는 앵커 PEM에 대한 정규 SHA-256이며,
containsFingerprint()는 DER SHA-256으로 멤버십을 답합니다. - 검증기는 구성된 앵커만 신뢰합니다.
KeyInfo에서 발견된 인증서는 서명자 리프와 후보 중간 인증서 역할을 합니다. 체인은 깊이 8 이내에서 구성된 앵커에 도달해야 하며, 모든 링크는 시간적으로 유효해야 하고, 발급 인증서는basicConstraintscA=TRUE를 지녀야 합니다(keyUsage가 있을 때는keyCertSign도). - 검증 프로파일은 허용 목록입니다: SHA-256, SHA-384, 또는 SHA-512를 사용하는 RSA 또는 ECDSA; 다이제스트 방법 SHA-256, SHA-384, 또는 SHA-512; 배타적 정규화만; 그리고 목록을 덮는
ds:Reference에 정확히 enveloped 서명과 배타적 C14N 변환 쌍. 그 외의 모든 것은unsupported_algorithm또는unsupported_transform으로 실패합니다. TslFetcher는 비-HTTPS URL을 거부하고 어떤 송출 전에도 SSRF 가드를 적용합니다.NetworkPolicy::STRICT_OFFLINE하에서는 이전에 캐시된 본문을 제공하거나NetworkPolicyViolation을 발생시킵니다. 어떤 아웃바운드 요청도 결코 전송되지 않습니다.
엣지 케이스 및 실패 모드
섹션 제목: “엣지 케이스 및 실패 모드”- 오래된 목록.
verifyXml(),assertFresh(), 또는buildBundle()에서 발생하는TslParseException은 신뢰 소스를 사용할 수 없음을 의미합니다. 이를 서명 판정이 아니라 운영상의 갱신 실패로 취급하세요. - 비정규
nextUpdate. 명시적인Z또는 숫자 오프셋이 없는 값은 관대하게 파싱하는 대신 예외를 던집니다. ETSI TS 119 612 §5.1.3은 UTCZ형식을 요구합니다. 게이트는 명시적 숫자 오프셋도 허용하고 그 외의 모든 것은 거부합니다. - 사용 시점 드리프트.
verifyXml()은 검증 시점에 게이트합니다.nextUpdate를 지나 메모리에 보관된 문서는 이후의buildBundle($tsl, $now)게이트에서 여전히 실패합니다. - 비어 있는 앵커 구성.
TslSignatureVerifier는 빈 앵커 목록으로의 생성을 거부합니다(InvalidArgumentException). - 사용 가능한 서비스 없음. granted CA/QC 서비스가 없는 최신 목록은
buildBundle()에서TslParseException을 발생시킵니다. 빈 번들은 결코 생성되지 않습니다. - 오프라인 태세. 캐시된 본문이 없는
STRICT_OFFLINE은NetworkPolicyViolation을 발생시킵니다. 캐시 조회가 정책 확인에 선행하므로, 캐시된 목록은 에어갭 검증을 계속 작동하게 합니다. - 크기 초과 또는 빈 응답.
fetch()는 최대$maxBytes바이트(기본 16 MiB)를 읽습니다. 잘린 목록은 이후 다이제스트 검증에서 실패합니다. 빈 본문은TslFetchException을 발생시킵니다. - XML 내의 DOCTYPE. 모든 DOCTYPE는 libxml이 엔티티 테이블을 구축하기 전에 거부되며, 로드 후에도 다시 거부됩니다. 이는 XXE 및 엔티티 확장(billion-laughs) 입력 클래스를 차단합니다.
- 다중 서명. 검증된 enveloped
ds:Signature만 다이제스트 계산 전에 제거되며, 형제 서명과 카운터 서명은 보존됩니다. 추가 XAdES 참조는 허용되지만, 정확히 하나의ds:Reference가 문서 루트를 덮어야 합니다. - 만료된 체인 자료. 만료되었거나 아직 유효하지 않은 서명자, 중간 인증서, 또는 앵커는 사유
expired_anchor로 실패합니다.clockTolerance는 허용 범위를 대칭적으로 넓히며 기본값은0입니다.
FIPS 모드 동작
섹션 제목: “FIPS 모드 동작”검증기의 허용 목록은 SHA-2 계열을 사용하는 RSA와 ECDSA로 고정되어 있으며, SHA-1과 MD5는 구조적으로 배제됩니다. 서명 연산은 번들된 소프트웨어 암호화(phpseclib)에서 실행됩니다. NextPDF는 그 연산에 대해 어떤 FIPS 140-3 검증 주장도 하지 않습니다. Enterprise FIPS 140-3 암호 정책 프로파일은 보안 모듈과 함께 문서화되어 있습니다. 이는 알고리즘 선택을 제약하며 신뢰 목록 구조나 이 모듈의 fail-closed 동작을 변경하지 않습니다.
적합성
섹션 제목: “적합성”| 주장 | 표준 | 조항 |
|---|---|---|
| Next update가 지난 신뢰 목록은 만료된 것으로 폐기됩니다. | ETSI TS 119 612 | §5.3.15 |
날짜-시각 필드는 Z 지정자와 함께 UTC로 표현된 ISO 8601 문자열입니다. | ETSI TS 119 612 | §5.1.3 |
| 스킴 운영자는 진정성과 무결성을 위해 신뢰 목록에 서명합니다. | ETSI TS 119 612 | §5.7.1 |
| 적격 서비스 상태는 granted 또는 withdrawn 상태 URI입니다. | ETSI TS 119 612 | §5.5.4 |
적격 CA는 Svctype/CA/QC 서비스 타입 URI로 식별됩니다. | ETSI TS 119 612 | §5.5.1.1 |
| TSL 시퀀스 번호는 1에서 시작하여 각 릴리스마다 증가합니다. | ETSI TS 119 612 | §5.3.2 |
모든 조항은 의역되었습니다. NextPDF는 규범적 텍스트를 재현하지 않습니다. NextPDF는 어떤 ETSI TS 119 612 적합성 주장도, 어떤 eIDAS 인증 주장도 하지 않습니다. 신뢰 목록을 소비한다고 해서 서명, 인증서, 또는 NextPDF 출력물이 “적격”이 되지는 않습니다. 적격성은 회원국 감독 하에 있는 신뢰 서비스 제공자에게 속하며, 법적 효력은 이 모듈의 범위 밖입니다. XMLDSig 처리 모델 제약(enveloped 서명 변환, 배타적 정규화, 루트를 덮는 참조)은 제품의 검증 프로파일로부터 문서화되었습니다. W3C XML Signature 사양은 인용된 증거 집합 밖에 있습니다. 이 모듈은 목록이 신뢰 입력으로 허용 가능한지만 결정합니다. 결과 앵커에 대한 인증서 경로 검증은 인증서 검증 계층에 속합니다.
개발 참고
섹션 제목: “개발 참고”- 의존성은 PSR 인터페이스입니다: PSR-18 클라이언트, PSR-17 요청 팩토리, 그리고 선택적인 PSR-16 캐시. 테스트에서는 인메모리 더블을 주입하세요. cold
fetch()를 제외하면 어떤 단계도 라이브 네트워크 접근을 요구하지 않습니다. - 최상위 앵커는 대역 외로 고정하세요. 회원국 목록의 경우, LOTL 앵커가 목록 서명자를 인가합니다. 검증기는 결코
KeyInfo콘텐츠로부터 신뢰를 부트스트랩하지 않습니다. - 백그라운드 폴링, 피벗 LOTL 처리, 그리고 상호 TLS 또는 프록시 인증은 이 버전에서 fetcher의 범위 밖입니다. 갱신은 외부에서 스케줄링하고 각
nextUpdate전에 다시 fetch하세요. buildBundle()에는 생성 시점이 아니라 검증 시점을 전달하세요. 각 갱신 후에 번들을 다시 빌드하세요. 소스 목록의nextUpdate를 지나서 번들을 결코 캐시하지 마세요.bundleVersion은 관찰 가능한 형태tsl-<territory>-seq<sequenceNumber>를 가집니다.TslDocument의rawXmlSha256은 증거 기록과 재생 탐지를 지원합니다.- 잘못된 형식의 서비스 항목은 방어적 자리표시 값으로 파싱됩니다. 번들 생성에 도달하는 잘못된 형식의 디지털 신원은
InvalidArgumentException으로 fail-closed됩니다. - 이 클래스들은 패키지
@since 1.10.0소스 주석을 지닙니다(TslFetchException:3.2.0).TslDocument,TspService,TspServiceQualifier는 소스에@api주석이 있습니다.
함께 보기
섹션 제목: “함께 보기”- eIDAS 보증 수준 — 신뢰 목록 증거를 보증 수준에 매핑하는 기능 페이지.
- ASiC 컨테이너 — 컨테이너 신뢰 바인딩을 위한
TslTrustAnchorProvider::buildBundle()의 소비자. - 서명 검증 — 신뢰 앵커를 소비하는 AdES/PAdES 검증 측.
- 보안 — 심층 레퍼런스 — 결합된 Enterprise 보안 표면.
- 서명 — 심층 레퍼런스 — PAdES B-LT 및 B-LTA 장기 생성기.
발행 경계
섹션 제목: “발행 경계”이 페이지는 외부에서 관찰 가능한 동작과 지원되는 공개 API 표면만을 문서화합니다. 내부 네임스페이스 경로, 헬퍼 클래스, 메커니즘 테이블, 런북 파일명, 그리고 티켓 접두사는 범위 밖입니다.