Enterprise 版本
签名验证 — 深度参考
本页是 NextPDF Enterprise 中 AdES 验证侧面向的深度参考。入口点为 NextPDF\Enterprise\Security\Validation\AdESValidationEngine。它实现了 NextPDF 以 ETSI 建模的验证流程,涵盖基础、带时间、长期与归档时间戳检查:基础验证、带时间验证、带长期数据验证,以及归档 DocTimeStamp 覆盖链验证。结果为 ValidationReport 值,携带具有 ETSI URN 字符串值的 MainIndication 与 SubIndication 枚举分支。本页还记录了支撑面向:SignatureDataExtractor SPI 及其 CmsSignatureDataExtractor 实现、PdfSignatureDictionaryScanner 字节级扫描器、NextPDF\Enterprise\Security\Pki 路径验证面向,以及 BatchSignatureValidator。关于工作流层面的指引,参见 签名验证:AdES / PAdES 密码学验证侧。
可用性与许可
标题为“可用性与许可”的章节此能力随 NextPDF Enterprise(nextpdf/enterprise)一同发布,并通过 Enterprise 层级许可证信封激活。缺少该授权的部署不会加载该能力的类。比较各版本并获取许可证。
公共 API 面向
标题为“公共 API 面向”的章节| 符号 | 参数 | 默认行为 | 返回 | 抛出或失败于 | 说明 |
|---|---|---|---|---|---|
AdESValidationEngine::__construct | 11 个可选参数:?PathValidatorInterface $chainValidator、?SignatureDataExtractor $extractor、ClockInterface $clock、?LoggerInterface $logger、string $defaultPolicy、NetworkPolicy $networkPolicy,以及五个可选验证协作者 | 所有默认值均为失败即关闭:基于引擎时钟的 Pki 路径验证器、无提取器、无 TSA 信任存储 | 新引擎 | 不抛出 | 在无信任存储时,TSA 链评估报告为不受信;该结果映射为 INDETERMINATE,绝不通过 |
AdESValidationEngine::validateBasic | string $signedData、string $signature | 基础验证:格式、摘要、密码学、弱算法、链、按来源门控的吊销 | ValidationReport | 不抛出;提取与路径失败映射为失败即关闭的报告 | 无提取器时仅执行守卫检查;参见边界情形 |
AdESValidationEngine::validateWithTime | string $signedData、string $signature、DateTimeImmutable $claimedTime | 先执行基础验证;证书有效窗口与吊销以声明时间为准进行比对 | ValidationReport | 不抛出 | 当签名时间戳属性存在时执行严格门控;$claimedTime 保持为时间锚点 |
AdESValidationEngine::validateWithLongTermData | string $signedData、string $signature、array $dssData(certs/ocsps/crls) | 需先通过基础验证;以 TSA-at-genTime 装备的签名时间戳门控;POE、DSS 吊销与归档门控 | ValidationReport | 不抛出 | NetworkPolicy::STRICT_OFFLINE 在嵌入数据不足时产出 INDETERMINATE / TRY_LATER |
AdESValidationEngine::validateArchivalTimestampChain | string $pdfBytes、array $dssData = []、?TrustAnchorStoreInterface $anchors = null | 基于证据、覆盖精确 ByteRange 字节的 DocTimeStamp 覆盖链 | ValidationReport | 面对恶意字节不抛出 | 仅对受信且覆盖至 EOF 的链返回 TOTAL_PASSED |
MainIndication | — | 以字符串为后备的枚举,三个分支 | — | — | ETSI URN 值;参见下方分支列表 |
SubIndication | — | 以字符串为后备的枚举,十五个分支 | — | — | ETSI URN 值;参见下方分支列表 |
ValidationReport::__construct | MainIndication $mainIndication、?SubIndication $subIndication、DiagnosticData $diagnosticData、DateTimeImmutable $validationTime、string $validationPolicy = '' | 不可变(final readonly)的验证结果 | 新报告 | 不抛出 | isPassed()、isFailed()、isIndeterminate()、toArray() |
DiagnosticData::__construct | array $certificateChain、array $timestamps、array $revocationData、string $validationPolicy、string $signatureFormat、array $warnings(均有默认值) | 不可变的证据容器;仅作审计轨迹 | 新值 | 不抛出 | toArray() 序列化引用以供报告 |
SignatureDataExtractor::extract | string $signedData、string $signature | SPI:解析 CMS 并提取验证组件 | ExtractedSignatureData | 无法解析签名时抛出 SignatureExtractionException | 接口;将 ASN.1 解析与引擎解耦 |
CmsSignatureDataExtractor::extract | string $signedData、string $signature | 提取并对分离式 PAdES 基础签名进行密码学验证 | ExtractedSignatureData | 仅当 CMS 完全无法解析时抛出 SignatureExtractionException | 密码学或绑定失败会返回 cryptoValid / hashValid 为 false 的数据;对此绝不抛出 |
PdfSignatureDictionaryScanner::scan | string $pdfBytes | 对 /ByteRange + /Contents 字典进行字节级扫描,并作精确契合的反欺骗交叉核验 | list<PdfSignatureOccurrence> | 全域;绝不抛出;畸形候选会被跳过 | 按覆盖终点排序,最早者在前 |
PathValidatorInterface::validate | array $chain、?DateTimeImmutable $validationTime = null、array $initialPolicies = [] | RFC 5280 §6.1.4 路径验证及策略处理 | PathValidationResult | 链在结构上无效或触碰对抗性限额时抛出 PathValidationException | 链以末端实体在前、锚点在后 |
PathValidatorInterface::validateWithAiaChasing | array $chain、?DateTimeImmutable $validationTime = null | 通过 AIA 解析缺失的中间证书,然后验证 | PathValidationResult | PathValidationException | 抓取受超时与字节数上限约束 |
CertificateChainValidator | 构造函数:引擎、PathValidationOptions、时钟、日志器;静态 withDefaults() | 带默认对抗性上限的 SPI 实现 | 两个方法均返回 PathValidationResult | PathValidationException | 当 OpenSSLCertificate 无法导出为 PEM 时也会抛出 |
PathValidationOptions::__construct | 上限(maxDepth、maxPolicyFanout、fetchTimeoutSeconds、fetchSizeCapBytes)加上策略标志、?TrustAnchorStoreInterface $trustAnchors、bool $requireTrustedAnchor | 深度 32、扇出 64、每次抓取 5 秒、每次抓取 10 MiB;所有标志为 false | 新选项 | 不抛出 | 工厂方法:defaults()、strict()、withTrustAnchors() |
PathValidationResult::__construct | bool $valid、string $trustAnchorFingerprint、DateTimeImmutable $validatedAt、array $validPolicies、?RevocationCheckResult $revocation、bool $trustAnchorTrusted、array $fetchedCertificates、array $failureReasons | 不可变结果;trustAnchorTrusted 默认为 false(失败即关闭) | 新值 | 不抛出 | 信任成员资格有别于结构有效性 |
PolicyProcessor | 构造函数:PolicyTreeState $state、PathValidationOptions $options;processCertificate(string $certDer, int $depth, bool $selfIssued)、finalizeWrapUp()、tree() | RFC 5280 §6.1.4 策略树扩展、映射与收尾 | void / list<non-empty-string> / PolicyTree | 任一策略处理失败时抛出 PathValidationException(失败即关闭) | 收尾返回存活的策略 OID,不含 anyPolicy |
PolicyTree | attach(PolicyTreeNode $node, PathValidationOptions $options)、enforceFanout(...)、remove(...),以及只读查询 | 带深度索引的 valid_policy_tree 状态 | 各方法有所不同 | 当存活叶子数超过扇出上限时抛出 PathValidationException | 暴露 ANY_POLICY_OID(2.5.29.32.0) |
NameConstraintsChecker::processCertificate | string $certDer、bool $applyNameCheck | 按 RFC 5280 §6.1.4(g) 累积并强制执行允许 / 排除子树 | void | 违反子树、约束中出现不支持的 GeneralName 形式,或触碰上限时抛出 PathValidationException | 不可比较的名称以失败即关闭方式处理 |
TrustAnchorStoreInterface::containsFingerprint | string $anchorDerSha256Hex | 以锚点 DER 证书的小写十六进制 SHA-256 判定成员资格 | bool | 不抛出 | 由路径验证器咨询的信任接缝 |
BatchSignatureValidator::validate | array $inputs(list<DocumentSignatureInput>) | 多文档签名验证,带每批次吊销缓存 | BatchValidationReport | 列表为空时抛出 InvalidArgumentException;资源守卫拒绝超过 1000 个文档的批次 | 位于 NextPDF\Enterprise\Signature |
final class AdESValidationEnginepublic function validateBasic(string $signedData, string $signature): ValidationReportpublic function validateWithTime( string $signedData, string $signature, DateTimeImmutable $claimedTime,): ValidationReportpublic function validateWithLongTermData( string $signedData, string $signature, array $dssData,): ValidationReportpublic function validateArchivalTimestampChain( string $pdfBytes, array $dssData = [], ?TrustAnchorStoreInterface $anchors = null,): ValidationReportpublic function validate( array $chain, ?DateTimeImmutable $validationTime = null, array $initialPolicies = [],): PathValidationResult;public function validateWithAiaChasing( array $chain, ?DateTimeImmutable $validationTime = null,): PathValidationResult;public static function withDefaults( ?ClockInterface $clock = null, ?AiaChaser $aiaChaser = null, ?LoggerInterface $logger = null,): selfpublic function containsFingerprint(string $anchorDerSha256Hex): bool;public function extract(string $signedData, string $signature): ExtractedSignatureData;public function scan(string $pdfBytes): arraypublic function validate(array $inputs): BatchValidationReport指示枚举。 MainIndication 分支:TOTAL_PASSED、TOTAL_FAILED、INDETERMINATE。后备值遵循 urn:etsi:019102:mainindication:total-passed 的模式(小写、连字符分隔)。SubIndication 分支:HASH_FAILURE、SIG_CRYPTO_FAILURE、REVOKED、EXPIRED、NOT_YET_VALID、NO_POE、TRY_LATER、CERTIFICATE_CHAIN_GENERAL_FAILURE、FORMAT_FAILURE、REVOKED_CA_NO_POE、CRYPTO_CONSTRAINTS_FAILURE、POLICY_PROCESSING_FAILURE、REVOCATION_OUT_OF_BOUNDS_NO_POE、NO_SIGNING_CERTIFICATE_FOUND、TIMESTAMP_ORDER_FAILURE。每一分支均以 urn:etsi:019102:subindication:<CASE_NAME> 为后备,含精确的分支名称。
行为契约
标题为“行为契约”的章节- 报告进,报告出。 四个引擎入口点对恶意输入返回
ValidationReport而非抛出。被捕获的SignatureExtractionException转入守卫路径;被捕获的PathValidationException映射为TOTAL_FAILED/CERTIFICATE_CHAIN_GENERAL_FAILURE。 - 基础验证顺序。 先做格式检查;不可解析的结构为
TOTAL_FAILED/FORMAT_FAILURE(EN 319 102-1 §5.3.4)。随后是摘要(HASH_FAILURE)与密码学验证(SIG_CRYPTO_FAILURE),与 EN 319 102-1 §5.2.7.4 的构件结果相符。摘要由验证器重新计算并与messageDigest签名属性比对(RFC 5652 §5.6);生产方提供的摘要绝不受信。 - 弱算法降级。 在 SHA-1 下通过、或带弱签名证书绑定而通过的签名,返回
INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE,绝不为TOTAL_PASSED。时间路径会重新断言这一点,使得弱签名绝不会被洗为一次时间有效的通过。 - 吊销来源门控。 仅当提取器确实执行了吊销检查(
revocationChecked为 true)时才咨询提取器的吊销标志。未检查的默认值既非”已验证未吊销”,也不构成REVOKED触发。吊销证据由 DSS 路径确立。 - 非通过的传播。 时间与长期路径绝不会将非通过的基础结果升级。存在一个例外:基础
INDETERMINATE/REVOKED会以$claimedTime为准解析;在声明时间当刻或之前的吊销为TOTAL_FAILED/REVOKED。这映射了 EN 319 102-1 §5.3.4 中以时间证据解析吊销相关不确定结果的模式。当无法执行该比较时,未解析的基础报告会被原样传播。 - 严格签名时间戳绑定(失败即关闭;BC 破坏)。 当 CMS 携带
id-aa-timeStampToken未签名属性时,其存在会在时间与长期路径中同时触发强制执行;不存在仅告警模式。基数必须恰为一个属性、且恰含一个值(EN 319 122-1 §5.3);任何其他形态均为TOTAL_FAILED/FORMAT_FAILURE。该令牌必须端到端通过密码学验证;无法验证的令牌、解析器差异冲突,或印记不匹配均为INDETERMINATE/TIMESTAMP_ORDER_FAILURE。不支持的或 SHA-1 的印记算法为INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE。绑定规则遵循 RFC 3161 Appendix A:令牌的messageImprint必须等于 SignerInfosignature值字节的哈希,并以恒定时间比较。 - 长期路径门控。 在以第 5.4 条标注的路径中,绑定的签名时间戳还会在令牌的
genTime处额外接受 TSA 证书评估;不受信的锚点为INDETERMINATE/CERTIFICATE_CHAIN_GENERAL_FAILURE,绝不通过。NetworkPolicy::STRICT_OFFLINE在嵌入 DSS 材料不足时返回INDETERMINATE/TRY_LATER。存在性证明、DSS 吊销与归档链发现各自短路为带映射子指示的INDETERMINATE。 - 归档链门控。 不存在 DocTimeStamp 为
INDETERMINATE/NO_POE。结构不符合规范的 ByteRange 为TOTAL_FAILED/FORMAT_FAILURE。每个令牌都必须通过验证、将其印记绑定到 ByteRange 精确覆盖的字节,并通过 TSA-at-genTime 侧面映射(EXPIRED、NOT_YET_VALID、REVOKED_CA_NO_POE、CERTIFICATE_CHAIN_GENERAL_FAILURE,或在严格离线下的TRY_LATER)。顺序被强制执行:genTime非递减、覆盖严格推进,且后续令牌须包含前一令牌的/Contents空洞。最新令牌必须覆盖最后一个字节;尾随字节为TIMESTAMP_ORDER_FAILURE。genTime超前验证器时钟逾 300 秒为TIMESTAMP_ORDER_FAILURE。 - 诊断从不决策。
DiagnosticData::$timestamps的存在性证明条目仅为审计轨迹。它们绝不改变指示,且累加器在每个入口点重置。 - Pki 限额先于密码学。
PathValidationOptions上限(深度 32、策略扇出 64、每次抓取 5 秒与 10 MiB)在昂贵工作之前被检查。PathValidationResult::$trustAnchorTrusted有别于$valid;requireTrustedAnchor使未经确认的终端无效。strict()会启用requireExplicitPolicy、硬失败的吊销传输,以及requireTrustedAnchor。路径有效性以锚点为相对基准,符合 RFC 5280 §6.1:有效路径始于作为输入提供的信任锚。 - 批处理面向。
BatchSignatureValidator::validate()对空列表抛出InvalidArgumentException,并通过资源守卫拒绝超过 1000 个文档的批次。在该管线中,PHP 掌管全部密码学验证。
边界情形与失败模式
标题为“边界情形与失败模式”的章节- 默认引擎无提取器。
new AdESValidationEngine()仅执行守卫检查:空签名或空签名数据为TOTAL_FAILED;任何非空对解析为INDETERMINATE/NO_SIGNING_CERTIFICATE_FOUND,绝不为TOTAL_PASSED。注入NextPDF\Enterprise\Security\Validation\CmsSignatureDataExtractor以获得密码学验证。 - 默认 TSA 信任检查无存储。 此时每条 TSA 链都报告为不受信,因此归档与长期签名时间戳结果保持
INDETERMINATE。通过validateArchivalTimestampChain(..., $anchors)或经配置的TsaCertificateAtGenTimeCheck提供锚点。 - 空
$pdfBytes。validateArchivalTimestampChain('')返回TOTAL_FAILED/FORMAT_FAILURE。 - 修复前的签名时间戳无法通过。 由严格绑定修复之前的 NextPDF 版本所产生的令牌印记了不同的输入。它们永久无法通过 Appendix A 绑定;请重新签名并重新加时间戳以恢复肯定结果。这是一次有意的、有记录的 BC 破坏。
- 重复或重叠的 DocTimeStamp。 同一修订中的重复项、相等或重叠的覆盖,或不包含前一令牌签名空洞的后续令牌,均会通不过顺序门控。
- 扫描器为全域且字节级。
scan()会静默跳过畸形或伪造的候选;内容流内部的诱饵/ByteRange会被拒绝。它不解析间接对象,也不遍历交叉引用表。 - 覆盖,而非可达性。
validateArchivalTimestampChain()证明到文件末尾的密码学字节范围覆盖。对象级可达性分析(例如,某个被覆盖修订内部被重新指向的文档根)声明为超出范围。 - 直接使用 Pki 会抛出。 直接调用
PathValidatorInterface实现会对结构上无效的链、触碰上限、不支持的约束形式,以及OpenSSLCertificate句柄的 PEM 导出失败暴露PathValidationException。引擎捕获此类异常;你自己的调用方必须处理它。
FIPS 模式行为
标题为“FIPS 模式行为”的章节验证侧接受带 SHA-2 的 RSA PKCS#1 v1.5,以及 P-256/P-384/P-521 上的 ECDSA。RSASSA-PSS、EdDSA 与 SHA-3 令牌作为不支持而失败即关闭;SHA-1 降级为 CRYPTO_CONSTRAINTS_FAILURE。在 Enterprise FIPS 140-3 密码策略配置文件下(随安全模块一同记录),该约束作用于接受哪些算法;验证流程本身——摘要重算、签名检查、绑定、路径验证——保持不变。NextPDF 不持有 FIPS 140-3 证书,本页也不作此声明。
合规性
标题为“合规性”的章节| 主张 | 标准 | 条款 |
|---|---|---|
| 基础签名验证是可复用于时间戳与带时间验证的构件。 | ETSI EN 319 102-1 | §5.3.1 |
完整性失败映射为 HASH_FAILURE;签名检查失败映射为 SIG_CRYPTO_FAILURE。 | ETSI EN 319 102-1 | §5.2.7.4 |
| 格式检查先运行,非通过即停止流程。 | ETSI EN 319 102-1 | §5.3.4 |
| 吊销相关的不确定结果可用时间证据解析。 | ETSI EN 319 102-1 | §5.3.4 |
| 有效的证书路径始于作为输入提供的信任锚。 | RFC 5280 | §6.1 |
验证器重算内容摘要;它必须等于 messageDigest 签名属性。 | RFC 5652 | §5.6 |
签名时间戳的 messageImprint 对 SignerInfo signature 字段值进行哈希。 | RFC 3161 | Appendix A |
signature-time-stamp 属性携带恰好一个 AttributeValue。 | ETSI EN 319 122-1 | §5.3 |
所有条款均为转述;NextPDF 不复制规范性文本。NextPDF 不作任何 AdES / PAdES 合规或认证声明。 支持某项标准不等于遵从它,遵从不等于认证——NextPDF 不持有任何认证,也不授予任何认证。引擎将所引证的验证程序作为一种能力实现;它不是合格或经认证的验证服务,而 TOTAL_PASSED 报告是一项密码学陈述,而非法律裁定。这些枚举值复用 ETSI URN 标识符模式以实现报告数据的互操作性;该复用不断言任何背书。
开发说明
标题为“开发说明”的章节- 条款标签映射。 包源码将入口点标注为 EN 319 102-1 的 5.2、5.3 与 5.4 条。合规语料库将基础签名验证流程本身置于 5.3 条,而密码学构件置于 5.2.7.4。本页引用检索到的条款编号;具有权威性的是行为契约,而非标签。
- 确定性测试。 每一次时间比较都流经注入的 PSR-20
ClockInterface。注入一个冻结时钟以测试窗口检查、300 秒的 genTime 偏移界限,以及 CRL 新鲜度决策。 - 组合。 所有引擎协作者均由构造函数注入且可选,带失败即关闭的默认值。默认路径验证器为基于引擎时钟的
CertificateChainValidator::withDefaults();默认选项使得对于符合规范、无约束的输入,策略与名称约束处理为空操作。 - 命名空间。 引擎面向位于
NextPDF\Enterprise\Security\Validation,路径验证面向位于NextPDF\Enterprise\Security\Pki,批处理编排器位于NextPDF\Enterprise\Signature。 - 报告卫生。 报告不可变且可经
toArray()序列化。诊断上下文在每个入口点重置,因此报告绝不会携带同一引擎实例上一次运行的证据。
- 签名验证:AdES / PAdES 密码学验证侧 — 能力页:工作流、算法表、升级说明。
- 签名 — 深度参考 — PAdES B-LT / B-LTA 生产方。
- 验证 — 深度参考 — 不含密码学的结构策略检查。
- 安全 — 深度参考 — 合并的 Enterprise 安全面向,含 FIPS 配置文件。
- PAdES 基线映射 — 跨版本的 B-B、B-T、B-LT、B-LTA。
发布边界
标题为“发布边界”的章节本页仅记录外部可观察的行为与受支持的公共 API 面向。内部命名空间路径、辅助类、机制表、运行手册文件名与工单前缀均超出范围。