跳转到内容
getnextpdf.com

Enterprise 版本

受信任列表 — 深度参考

本页是 NextPDF Enterprise 中受信任列表能力面的深度参考。该能力面即 NextPDF\Enterprise\Security\Tsl 命名空间的十二个公开类。NextPDF\Enterprise\Security\Tsl\TslPolicyEnforcer 是经过编排的入口点:仅当 HTTP 获取、XMLDSig 验证、结构解析与 nextUpdate 陈旧度闸门全部通过时,它才返回一个 TslDocument。随后 TslTrustAnchorProvider::buildBundle() 从活跃的 CA/QC 服务派生出一个信任锚捆绑包,并在抽取任何锚之前,以调用方提供的时刻重新断言新鲜度。每一次失败都会抛出一个带类型的异常;没有任何阶段会静默降级。该流水线支持在调用方提供时验证欧盟成员国受信任列表以及源自 LOTL(受信任列表之列表,List of Trusted Lists)的信任锚;自动 LOTL 发现、轮询与 pivot 处理不在范围之内。

此能力随 NextPDF Enterprisenextpdf/enterprise)发布,并在 Enterprise 级许可证信封下激活。不具备该授权的部署不会加载此能力的类。比较各版本并获取许可证

符号参数默认行为返回抛出或失败于备注
TslPolicyEnforcerTslFetcher $fetcherTslSignatureVerifier $verifierTslXmlParser $parser将获取、签名验证、解析与陈旧度闸门合并为单一入口点传播下述流水线异常final;按构造即 fail-closed
TslPolicyEnforcer::fetchAndVerifystring $url获取一份 TSL,然后对字节运行 verifyXml()TslDocumentTslFetchExceptionNetworkPolicyViolationTslSignatureExceptionTslParseException仅当全部四个阶段通过时返回
TslPolicyEnforcer::verifyXmlstring $xml验证签名、解析并拒绝陈旧列表TslDocumentTslSignatureExceptionTslParseException陈旧度以当前系统时间为准评估
TslFetcherClientInterface $httpClientRequestFactoryInterface $requestFactory?CacheInterface $cache = nullint $defaultTtlSeconds = 3600int $maxBytes = 16_777_216NetworkPolicy $networkPolicy = NetworkPolicy::ONLINE仅限 HTTPS 的 TSL/LOTL 检索,带基于 ETag 的缓存final;SSRF 防护封禁私有、回环、链路本地与元数据主机,并缓解 DNS 重绑定
TslFetcher::fetchstring $urlIf-None-Match 重新验证的 GET;在配置的 TTL 下缓存正文与 ETagstring(原始 XML 字节)TslFetchExceptionNetworkPolicyViolation至多读取 $maxBytes 字节;在 STRICT_OFFLINE 下仅提供缓存正文
TslSignatureVerifierarray $trustAnchorsPemint $clockTolerance = 0固定至所配置信任锚的 XMLDSig 验证器锚列表为空时抛出 InvalidArgumentExceptionfinal;在 ALLOWED_SIG_ALGALLOWED_DIGEST_ALG 中设允许清单
TslSignatureVerifier::verifystring $xmlfail-closed 地验证 enveloped XMLDSig 签名string(签名者证书 PEM)TslSignatureException,带机器可读的原因码KeyInfo 证书本身永不受信任;签名者必须链接到某个已配置的锚
TslXmlParser::parsestring $xml结构化解析为 TslDocument;与签名无关TslDocumentTslParseException在解析前 fail-closed 地拒绝任何 DOCTYPE;以 LIBXML_NONET 加载;调用方必须先验证再信任结果
TslTrustAnchorProvider::buildBundleTslDocument $tslDateTimeImmutable $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 $qualifierUristring $criteriaListAssert = 'all'array $policyOidConditions = []array $keyUsageConditions = []一个带可选条件的 ETSI 服务限定符final readonly;常量 FOR_ESIGFOR_ESEALFOR_WSAQSCD_STATEMENTNO_QSCD
EnterpriseCaTrustAnchorBundlearray $anchorsPemstring $bundleVersionstring $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 $reasonstring $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_signatureuntrusted_signerinvalid_signaturedigest_mismatchunsupported_algorithmunsupported_transformexpired_anchor

  • 流水线顺序固定:获取、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 之内到达某个已配置的锚,每一环必须在时间上有效,且签发证书必须携带 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,并在任何出网之前施加 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 构建实体表之前被拒绝,加载之后再次拒绝。这封堵了 XXE 与实体扩展(billion-laughs)这类输入类别。
  • 多重签名。 只有经过验证的 enveloped ds:Signature 会在摘要计算之前被移除;兄弟签名与反签名予以保留。允许额外的 XAdES 引用,但必须恰有一个 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
方案运营方为真实性与完整性签名受信任列表。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-signature 变换、exclusive canonicalization、覆盖根的引用)依据产品的验证配置记录;W3C XML Signature 规范不在所引证据集之内。本模块只决定一份列表是否可作为信任输入被接受;针对由此得到的锚进行证书路径验证归属于证书验证层。

  • 依赖为 PSR 接口:一个 PSR-18 客户端、一个 PSR-17 请求工厂,以及一个可选的 PSR-16 缓存。测试中注入内存替身;除冷 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 面。内部命名空间路径、辅助类、机制表、运行手册文件名与工单前缀均不在范围之内。