跳到內容
getnextpdf.com

Enterprise 版本

信任清單 — 深入參考

本頁是 NextPDF Enterprise 中信任清單介面的深度參考。此介面即 NextPDF\Enterprise\Security\Tsl 命名空間的十二個公開類別。NextPDF\Enterprise\Security\Tsl\TslPolicyEnforcer 是經過編排的進入點:只有在 HTTP 抓取、XMLDSig 驗證、結構解析以及 nextUpdate 過期閘門全部通過時,它才回傳一個 TslDocumentTslTrustAnchorProvider::buildBundle() 接著從有效的 CA/QC 服務推導出信任錨集合,並在擷取任何錨之前,於呼叫端提供的時間點重新確認新鮮度。每一次失敗都會拋出具型別的例外;沒有任何階段會靜默降級。此管線支援驗證 EU 成員國信任清單,以及在呼叫端提供時使用來自 LOTL(List of Trusted Lists)的信任錨;自動 LOTL 探索、輪詢與 pivot 處理不在範圍內。

此能力隨 NextPDF Enterprisenextpdf/enterprise)出貨,並以 Enterprise 級授權封套啟用。未具備該權利的部署不會載入此能力的類別。比較版本並取得授權

符號參數預設行為回傳拋出或失敗於備註
TslPolicyEnforcerTslFetcher $fetcher, TslSignatureVerifier $verifier, TslXmlParser $parser將抓取、簽章驗證、解析與過期閘門結合為單一進入點傳遞下列管線例外final;由建構本身即 fail-closed
TslPolicyEnforcer::fetchAndVerifystring $url抓取一份 TSL,然後對其位元組執行 verifyXml()TslDocumentTslFetchException, NetworkPolicyViolation, TslSignatureException, TslParseException只有四個階段全部通過時才回傳
TslPolicyEnforcer::verifyXmlstring $xml驗證簽章、解析,並拒絕過期清單TslDocumentTslSignatureException, TslParseException過期性以目前系統時間為準評估
TslFetcherClientInterface $httpClient, RequestFactoryInterface $requestFactory, ?CacheInterface $cache = null, int $defaultTtlSeconds = 3600, int $maxBytes = 16_777_216, NetworkPolicy $networkPolicy = NetworkPolicy::ONLINE僅限 HTTPS 的 TSL/LOTL 取得,帶 ETag 快取final;SSRF 防護以 DNS 重繫結緩解阻擋私有、迴路、link-local 與 metadata 主機
TslFetcher::fetchstring $urlIf-None-Match 重新驗證的 GET;在設定的 TTL 下快取主體加 ETagstring(原始 XML 位元組)TslFetchException, NetworkPolicyViolation至多讀取 $maxBytes 位元組;在 STRICT_OFFLINE 下僅提供快取主體
TslSignatureVerifierarray $trustAnchorsPem, int $clockTolerance = 0釘選至已設定信任錨的 XMLDSig 驗證器錨清單為空時拋出 InvalidArgumentExceptionfinal;於 ALLOWED_SIG_ALGALLOWED_DIGEST_ALG 中設置允許清單
TslSignatureVerifier::verifystring $xml以 fail-closed 方式驗證 enveloped XMLDSig 簽章string(簽署者憑證 PEM)TslSignatureException,附帶機器可讀的原因碼KeyInfo 憑證本身絕不被信任;簽署者必須鏈接至已設定的錨
TslXmlParser::parsestring $xml結構性解析為 TslDocument;與簽章無關TslDocumentTslParseException在解析前即以 fail-closed 拒絕任何 DOCTYPE;以 LIBXML_NONET 載入;呼叫端必須先驗證才可信任結果
TslTrustAnchorProvider::buildBundleTslDocument $tsl, DateTimeImmutable $now先斷言新鮮度,再收集有效 CA/QC 服務的憑證EnterpriseCaTrustAnchorBundleTslParseException新鮮度閘門先於任何錨擷取;空結果集會拋出
TslDocument八個提升為 readonly 的屬性(見建構子區塊)不可變的已解析 TSL 值物件final readonly;原始碼標註 @api
TslDocument::isStaleDateTimeImmutable $now在 fail-closed 的 UTC 解析後,將 nextUpdate$now 比較boolTslParseException需要明確的 Z 或數值偏移指示子
TslDocument::assertFreshDateTimeImmutable $now當清單過期或 nextUpdate 無法解析時拋出voidTslParseException消費端邊界的新鮮度閘門
TslDocument::servicesOfTypestring $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不拋出
TspServiceQualifierstring $qualifierUri, string $criteriaListAssert = 'all', array $policyOidConditions = [], array $keyUsageConditions = []一個帶選擇性條件的 ETSI 服務限定詞final readonly;常數 FOR_ESIG, FOR_ESEAL, FOR_WSA, QSCD_STATEMENT, NO_QSCD
EnterpriseCaTrustAnchorBundlearray $anchorsPem, string $bundleVersion, string $bundleSha256釘選錨的集合;於建構時以所提供的錨驗證所提供的摘要InvalidArgumentExceptionbuildBundle() 取得;不要手動建構;實作 TrustAnchorStoreInterface
EnterpriseCaTrustAnchorBundle::containsFingerprintstring $anchorDerSha256Hex以 DER 主體之十六進位 SHA-256 判定錨的成員資格bool不拋出
EnterpriseCaTrustAnchorBundle::computeBundleSha256array $anchorsPem對換行正規化後的 PEM 串接計算標準 SHA-256string不拋出static
TslFetchException表示 TSL 取得失敗final;繼承 RuntimeException
TslParseException表示結構性或新鮮度失敗final;繼承 RuntimeException
TslSignatureExceptionstring $reason, string $message以原因碼表示 XMLDSig 驗證失敗final;公開 readonly$reason(見下方原因碼)

TslPolicyEnforcer

public function fetchAndVerify(string $url): TslDocument
public function verifyXml(string $xml): TslDocument

TslFetcher

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): string

TslSignatureVerifier

public function __construct(private readonly array $trustAnchorsPem, private readonly int $clockTolerance = 0)
public function verify(string $xml): string

TslXmlParser

public function parse(string $xml): TslDocument

TslTrustAnchorProvider

public function buildBundle(TslDocument $tsl, DateTimeImmutable $now): EnterpriseCaTrustAnchorBundle

TslDocument

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): bool
public function assertFresh(DateTimeImmutable $now): void
public function servicesOfType(string $serviceTypeIdentifier): array
public function activeServices(): array

TspService

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(): bool
public function isQualifiedCa(): bool

TspServiceQualifier

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): bool
public static function computeBundleSha256(array $anchorsPem): string

TslSignatureException

public function __construct(public readonly string $reason, string $message)

TslSignatureException 原因碼: missing_signature, untrusted_signer, invalid_signature, digest_mismatch, unsupported_algorithm, unsupported_transform, expired_anchor

  • 管線順序是固定的:抓取、XMLDSig 驗證、結構解析、過期閘門。TslPolicyEnforcer 只有在四者全部成功時才回傳一個 TslDocument。信任清單由其 scheme operator 簽署,讓信賴方能夠檢查真實性與完整性 — 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。
  • 集合版本由 scheme territory 與 TSL sequence number 推導而來。sequence number 在各次發行間單調遞增 — ETSI TS 119 612 §5.3.2。集合摘要是對各錨 PEM 計算的標準 SHA-256,containsFingerprint() 以 DER SHA-256 回答成員資格。
  • 驗證器只信任已設定的錨。在 KeyInfo 中找到的憑證作為簽署者 leaf 與候選中繼;鏈必須在深度 8 內抵達已設定的錨,每個環節都必須在時間上有效,且簽發憑證必須帶有 basicConstraints cA=TRUE(當 keyUsage 存在時另加 keyCertSign)。
  • 驗證設定檔為允許清單:RSA 或 ECDSA 搭配 SHA-256、SHA-384 或 SHA-512;摘要方法為 SHA-256、SHA-384 或 SHA-512;僅限 exclusive canonicalization;且在涵蓋清單的 ds:Reference 上恰好是 enveloped-signature 加上 exclusive-C14N 的轉換配對。其他任何情況皆以 unsupported_algorithmunsupported_transform 失敗。
  • TslFetcher 拒絕非 HTTPS 的 URL,並在任何 egress 之前套用 SSRF 防護。在 NetworkPolicy::STRICT_OFFLINE 下,它提供先前快取的主體,或拋出 NetworkPolicyViolation;絕不送出任何對外請求。
  • 過期清單。 來自 verifyXml()assertFresh()buildBundle()TslParseException 意指信任來源不可用。將其視為操作面的重整失敗,而非簽章裁決。
  • 非標準的 nextUpdate 沒有明確 Z 或數值偏移的值會拋出,而非寬鬆解析。ETSI TS 119 612 §5.1.3 要求 UTC 的 Z 形式;此閘門也接受明確的數值偏移,並拒絕其餘所有形式。
  • 使用時點漂移。 verifyXml() 在驗證時設閘;一份在記憶體中持有並超過 nextUpdate 的文件,仍會在稍後的 buildBundle($tsl, $now) 閘門失敗。
  • 空的錨設定。 TslSignatureVerifier 拒絕以空錨清單建構(InvalidArgumentException)。
  • 無可用服務。 一份沒有 granted CA/QC 服務的新鮮清單,會使 buildBundle() 拋出 TslParseException;絕不會產生空集合。
  • 離線姿態。 STRICT_OFFLINE 且無快取主體時拋出 NetworkPolicyViolation。快取查詢先於政策檢查,因此已快取的清單能讓氣隙驗證持續運作。
  • 過大或空的回應。 fetch() 至多讀取 $maxBytes 位元組(預設 16 MiB);被截斷的清單接著會在下游的摘要驗證失敗。空主體會拋出 TslFetchException
  • XML 中的 DOCTYPE。 任何 DOCTYPE 都在 libxml 建立 entity table 之前被拒絕,載入後再次拒絕。這封閉了 XXE 與 entity-expansion(billion-laughs)輸入類別。
  • 多重簽章。 只有已驗證的 enveloped ds:Signature 會在摘要計算前被移除;同層簽章與 counter-signature 皆被保留。允許額外的 XAdES references,但必須恰好有一個 ds:Reference 涵蓋文件根。
  • 過期的鏈材料。 過期或尚未生效的簽署者、中繼或錨會以原因 expired_anchor 失敗。clockTolerance 對稱地放寬接受窗口,預設為 0

驗證器的允許清單固定為 RSA 與 ECDSA 搭配 SHA-2 家族;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
scheme operator 為真實性與完整性簽署信任清單。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 sequence number 從 1 開始,並在每次發行遞增。ETSI TS 119 612§5.3.2

所有條款皆為改述;NextPDF 不重現規範性文字。NextPDF 不作任何 ETSI TS 119 612 一致性聲明,也不作任何 eIDAS 認證聲明。 消費一份信任清單並不會使某個簽章、某張憑證或某份 NextPDF 輸出成為「qualified」;合格性歸屬於在成員國監督下的信任服務提供者,法律效力則在本模組之外。XMLDSig 處理模型的約束(enveloped-signature 轉換、exclusive canonicalization、涵蓋根的 reference)由產品的驗證設定檔記載;W3C XML Signature 規範不在被引用的證據集內。本模組只決定一份清單是否可作為信任輸入被接受;針對所得錨的憑證路徑驗證屬於憑證驗證層。

  • 相依皆為 PSR 介面:一個 PSR-18 client、一個 PSR-17 request factory,以及一個選擇性的 PSR-16 cache。在測試中注入記憶體替身;除了冷的 fetch() 之外,沒有任何階段需要實際的網路存取。
  • 帶外釘選頂層錨。對於成員國清單,LOTL 錨授權清單簽署者;驗證器絕不從 KeyInfo 內容啟動信任。
  • 背景輪詢、pivot-LOTL 處理,以及雙向 TLS 或代理驗證在本版本中皆不在 fetcher 的範圍內。於外部排程重整,並在每個 nextUpdate 之前重新抓取。
  • 傳入 buildBundle() 的是驗證時間點,而非建構時間點。在每次重整後重建集合;絕不將集合快取超過來源清單的 nextUpdate
  • bundleVersion 有可觀察的外形 tsl-<territory>-seq<sequenceNumber>TslDocument 上的 rawXmlSha256 支援證據記錄與重放偵測。
  • 格式錯誤的服務項目會以防禦性佔位值解析;抵達集合建構的格式錯誤數位身分會以 InvalidArgumentException fail-closed。
  • 這些類別帶有套件 @since 1.10.0 的原始碼標註(TslFetchException3.2.0)。TslDocumentTspServiceTspServiceQualifier 皆有原始碼標註 @api

本頁僅記載外部可觀察的行為與受支援的公開 API 介面。內部命名空間路徑、輔助類別、機制表、runbook 檔名與工單前綴皆不在範圍內。