Enterprise 版本
信任清單 — 深入參考
本頁是 NextPDF Enterprise 中信任清單介面的深度參考。此介面即 NextPDF\Enterprise\Security\Tsl 命名空間的十二個公開類別。NextPDF\Enterprise\Security\Tsl\TslPolicyEnforcer 是經過編排的進入點:只有在 HTTP 抓取、XMLDSig 驗證、結構解析以及 nextUpdate 過期閘門全部通過時,它才回傳一個 TslDocument。TslTrustAnchorProvider::buildBundle() 接著從有效的 CA/QC 服務推導出信任錨集合,並在擷取任何錨之前,於呼叫端提供的時間點重新確認新鮮度。每一次失敗都會拋出具型別的例外;沒有任何階段會靜默降級。此管線支援驗證 EU 成員國信任清單,以及在呼叫端提供時使用來自 LOTL(List of Trusted Lists)的信任錨;自動 LOTL 探索、輪詢與 pivot 處理不在範圍內。
供應狀況與授權
標題為「供應狀況與授權」的區段此能力隨 NextPDF Enterprise(nextpdf/enterprise)出貨,並以 Enterprise 級授權封套啟用。未具備該權利的部署不會載入此能力的類別。比較版本並取得授權。
公開 API 介面
標題為「公開 API 介面」的區段| 符號 | 參數 | 預設行為 | 回傳 | 拋出或失敗於 | 備註 |
|---|---|---|---|---|---|
TslPolicyEnforcer | TslFetcher $fetcher, TslSignatureVerifier $verifier, TslXmlParser $parser | 將抓取、簽章驗證、解析與過期閘門結合為單一進入點 | — | 傳遞下列管線例外 | final;由建構本身即 fail-closed |
TslPolicyEnforcer::fetchAndVerify | string $url | 抓取一份 TSL,然後對其位元組執行 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 | 僅限 HTTPS 的 TSL/LOTL 取得,帶 ETag 快取 | — | — | final;SSRF 防護以 DNS 重繫結緩解阻擋私有、迴路、link-local 與 metadata 主機 |
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 | 以 fail-closed 方式驗證 enveloped XMLDSig 簽章 | string(簽署者憑證 PEM) | TslSignatureException,附帶機器可讀的原因碼 | KeyInfo 憑證本身絕不被信任;簽署者必須鏈接至已設定的錨 |
TslXmlParser::parse | string $xml | 結構性解析為 TslDocument;與簽章無關 | TslDocument | TslParseException | 在解析前即以 fail-closed 拒絕任何 DOCTYPE;以 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 主體之十六進位 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。
行為合約
標題為「行為合約」的區段- 管線順序是固定的:抓取、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 內抵達已設定的錨,每個環節都必須在時間上有效,且簽發憑證必須帶有basicConstraintscA=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_algorithm或unsupported_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。
FIPS 模式行為
標題為「FIPS 模式行為」的區段驗證器的允許清單固定為 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支援證據記錄與重放偵測。- 格式錯誤的服務項目會以防禦性佔位值解析;抵達集合建構的格式錯誤數位身分會以
InvalidArgumentExceptionfail-closed。 - 這些類別帶有套件
@since 1.10.0的原始碼標註(TslFetchException:3.2.0)。TslDocument、TspService與TspServiceQualifier皆有原始碼標註@api。
- eIDAS 保證等級 — 將信任清單證據對映至 Levels of Assurance 的能力頁。
- ASiC 容器 —
TslTrustAnchorProvider::buildBundle()的消費者,用於容器信任綁定。 - 簽章驗證 — 消費信任錨的 AdES/PAdES 驗證端。
- 安全 — 深度參考 — 合併後的 Enterprise 安全介面。
- 簽章 — 深度參考 — PAdES B-LT 與 B-LTA 的長期產生器。
出版邊界
標題為「出版邊界」的區段本頁僅記載外部可觀察的行為與受支援的公開 API 介面。內部命名空間路徑、輔助類別、機制表、runbook 檔名與工單前綴皆不在範圍內。