跳转到内容
getnextpdf.com

Enterprise 版本

签名验证 — 深度参考

本页是 NextPDF Enterprise 中 AdES 验证侧面向的深度参考。入口点为 NextPDF\Enterprise\Security\Validation\AdESValidationEngine。它实现了 NextPDF 以 ETSI 建模的验证流程,涵盖基础、带时间、长期与归档时间戳检查:基础验证、带时间验证、带长期数据验证,以及归档 DocTimeStamp 覆盖链验证。结果为 ValidationReport 值,携带具有 ETSI URN 字符串值的 MainIndicationSubIndication 枚举分支。本页还记录了支撑面向:SignatureDataExtractor SPI 及其 CmsSignatureDataExtractor 实现、PdfSignatureDictionaryScanner 字节级扫描器、NextPDF\Enterprise\Security\Pki 路径验证面向,以及 BatchSignatureValidator。关于工作流层面的指引,参见 签名验证:AdES / PAdES 密码学验证侧

此能力随 NextPDF Enterprisenextpdf/enterprise)一同发布,并通过 Enterprise 层级许可证信封激活。缺少该授权的部署不会加载该能力的类。比较各版本并获取许可证

符号参数默认行为返回抛出或失败于说明
AdESValidationEngine::__construct11 个可选参数:?PathValidatorInterface $chainValidator?SignatureDataExtractor $extractorClockInterface $clock?LoggerInterface $loggerstring $defaultPolicyNetworkPolicy $networkPolicy,以及五个可选验证协作者所有默认值均为失败即关闭:基于引擎时钟的 Pki 路径验证器、无提取器、无 TSA 信任存储新引擎不抛出在无信任存储时,TSA 链评估报告为不受信;该结果映射为 INDETERMINATE,绝不通过
AdESValidationEngine::validateBasicstring $signedDatastring $signature基础验证:格式、摘要、密码学、弱算法、链、按来源门控的吊销ValidationReport不抛出;提取与路径失败映射为失败即关闭的报告无提取器时仅执行守卫检查;参见边界情形
AdESValidationEngine::validateWithTimestring $signedDatastring $signatureDateTimeImmutable $claimedTime先执行基础验证;证书有效窗口与吊销以声明时间为准进行比对ValidationReport不抛出当签名时间戳属性存在时执行严格门控;$claimedTime 保持为时间锚点
AdESValidationEngine::validateWithLongTermDatastring $signedDatastring $signaturearray $dssDatacerts/ocsps/crls需先通过基础验证;以 TSA-at-genTime 装备的签名时间戳门控;POE、DSS 吊销与归档门控ValidationReport不抛出NetworkPolicy::STRICT_OFFLINE 在嵌入数据不足时产出 INDETERMINATE / TRY_LATER
AdESValidationEngine::validateArchivalTimestampChainstring $pdfBytesarray $dssData = []?TrustAnchorStoreInterface $anchors = null基于证据、覆盖精确 ByteRange 字节的 DocTimeStamp 覆盖链ValidationReport面对恶意字节不抛出仅对受信且覆盖至 EOF 的链返回 TOTAL_PASSED
MainIndication以字符串为后备的枚举,三个分支ETSI URN 值;参见下方分支列表
SubIndication以字符串为后备的枚举,十五个分支ETSI URN 值;参见下方分支列表
ValidationReport::__constructMainIndication $mainIndication?SubIndication $subIndicationDiagnosticData $diagnosticDataDateTimeImmutable $validationTimestring $validationPolicy = ''不可变(final readonly)的验证结果新报告不抛出isPassed()isFailed()isIndeterminate()toArray()
DiagnosticData::__constructarray $certificateChainarray $timestampsarray $revocationDatastring $validationPolicystring $signatureFormatarray $warnings(均有默认值)不可变的证据容器;仅作审计轨迹新值不抛出toArray() 序列化引用以供报告
SignatureDataExtractor::extractstring $signedDatastring $signatureSPI:解析 CMS 并提取验证组件ExtractedSignatureData无法解析签名时抛出 SignatureExtractionException接口;将 ASN.1 解析与引擎解耦
CmsSignatureDataExtractor::extractstring $signedDatastring $signature提取并对分离式 PAdES 基础签名进行密码学验证ExtractedSignatureData仅当 CMS 完全无法解析时抛出 SignatureExtractionException密码学或绑定失败会返回 cryptoValid / hashValid 为 false 的数据;对此绝不抛出
PdfSignatureDictionaryScanner::scanstring $pdfBytes/ByteRange + /Contents 字典进行字节级扫描,并作精确契合的反欺骗交叉核验list<PdfSignatureOccurrence>全域;绝不抛出;畸形候选会被跳过按覆盖终点排序,最早者在前
PathValidatorInterface::validatearray $chain?DateTimeImmutable $validationTime = nullarray $initialPolicies = []RFC 5280 §6.1.4 路径验证及策略处理PathValidationResult链在结构上无效或触碰对抗性限额时抛出 PathValidationException链以末端实体在前、锚点在后
PathValidatorInterface::validateWithAiaChasingarray $chain?DateTimeImmutable $validationTime = null通过 AIA 解析缺失的中间证书,然后验证PathValidationResultPathValidationException抓取受超时与字节数上限约束
CertificateChainValidator构造函数:引擎、PathValidationOptions、时钟、日志器;静态 withDefaults()带默认对抗性上限的 SPI 实现两个方法均返回 PathValidationResultPathValidationExceptionOpenSSLCertificate 无法导出为 PEM 时也会抛出
PathValidationOptions::__construct上限(maxDepthmaxPolicyFanoutfetchTimeoutSecondsfetchSizeCapBytes)加上策略标志、?TrustAnchorStoreInterface $trustAnchorsbool $requireTrustedAnchor深度 32、扇出 64、每次抓取 5 秒、每次抓取 10 MiB;所有标志为 false新选项不抛出工厂方法:defaults()strict()withTrustAnchors()
PathValidationResult::__constructbool $validstring $trustAnchorFingerprintDateTimeImmutable $validatedAtarray $validPolicies?RevocationCheckResult $revocationbool $trustAnchorTrustedarray $fetchedCertificatesarray $failureReasons不可变结果;trustAnchorTrusted 默认为 false(失败即关闭)新值不抛出信任成员资格有别于结构有效性
PolicyProcessor构造函数:PolicyTreeState $statePathValidationOptions $optionsprocessCertificate(string $certDer, int $depth, bool $selfIssued)finalizeWrapUp()tree()RFC 5280 §6.1.4 策略树扩展、映射与收尾void / list<non-empty-string> / PolicyTree任一策略处理失败时抛出 PathValidationException(失败即关闭)收尾返回存活的策略 OID,不含 anyPolicy
PolicyTreeattach(PolicyTreeNode $node, PathValidationOptions $options)enforceFanout(...)remove(...),以及只读查询带深度索引的 valid_policy_tree 状态各方法有所不同当存活叶子数超过扇出上限时抛出 PathValidationException暴露 ANY_POLICY_OID2.5.29.32.0
NameConstraintsChecker::processCertificatestring $certDerbool $applyNameCheck按 RFC 5280 §6.1.4(g) 累积并强制执行允许 / 排除子树void违反子树、约束中出现不支持的 GeneralName 形式,或触碰上限时抛出 PathValidationException不可比较的名称以失败即关闭方式处理
TrustAnchorStoreInterface::containsFingerprintstring $anchorDerSha256Hex以锚点 DER 证书的小写十六进制 SHA-256 判定成员资格bool不抛出由路径验证器咨询的信任接缝
BatchSignatureValidator::validatearray $inputslist<DocumentSignatureInput>多文档签名验证,带每批次吊销缓存BatchValidationReport列表为空时抛出 InvalidArgumentException;资源守卫拒绝超过 1000 个文档的批次位于 NextPDF\Enterprise\Signature
final class AdESValidationEngine
public function validateBasic(string $signedData, string $signature): ValidationReport
public function validateWithTime(
string $signedData,
string $signature,
DateTimeImmutable $claimedTime,
): ValidationReport
public function validateWithLongTermData(
string $signedData,
string $signature,
array $dssData,
): ValidationReport
public function validateArchivalTimestampChain(
string $pdfBytes,
array $dssData = [],
?TrustAnchorStoreInterface $anchors = null,
): ValidationReport
public 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,
): self
public function containsFingerprint(string $anchorDerSha256Hex): bool;
public function extract(string $signedData, string $signature): ExtractedSignatureData;
public function scan(string $pdfBytes): array
public function validate(array $inputs): BatchValidationReport

指示枚举。 MainIndication 分支:TOTAL_PASSEDTOTAL_FAILEDINDETERMINATE。后备值遵循 urn:etsi:019102:mainindication:total-passed 的模式(小写、连字符分隔)。SubIndication 分支:HASH_FAILURESIG_CRYPTO_FAILUREREVOKEDEXPIREDNOT_YET_VALIDNO_POETRY_LATERCERTIFICATE_CHAIN_GENERAL_FAILUREFORMAT_FAILUREREVOKED_CA_NO_POECRYPTO_CONSTRAINTS_FAILUREPOLICY_PROCESSING_FAILUREREVOCATION_OUT_OF_BOUNDS_NO_POENO_SIGNING_CERTIFICATE_FOUNDTIMESTAMP_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 必须等于 SignerInfo signature 值字节的哈希,并以恒定时间比较。
  • 长期路径门控。 在以第 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 侧面映射(EXPIREDNOT_YET_VALIDREVOKED_CA_NO_POECERTIFICATE_CHAIN_GENERAL_FAILURE,或在严格离线下的 TRY_LATER)。顺序被强制执行:genTime 非递减、覆盖严格推进,且后续令牌须包含前一令牌的 /Contents 空洞。最新令牌必须覆盖最后一个字节;尾随字节为 TIMESTAMP_ORDER_FAILUREgenTime 超前验证器时钟逾 300 秒为 TIMESTAMP_ORDER_FAILURE
  • 诊断从不决策。 DiagnosticData::$timestamps 的存在性证明条目仅为审计轨迹。它们绝不改变指示,且累加器在每个入口点重置。
  • Pki 限额先于密码学。 PathValidationOptions 上限(深度 32、策略扇出 64、每次抓取 5 秒与 10 MiB)在昂贵工作之前被检查。PathValidationResult::$trustAnchorTrusted 有别于 $validrequireTrustedAnchor 使未经确认的终端无效。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。引擎捕获此类异常;你自己的调用方必须处理它。

验证侧接受带 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_FAILUREETSI 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 3161Appendix A
signature-time-stamp 属性携带恰好一个 AttributeValueETSI 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() 序列化。诊断上下文在每个入口点重置,因此报告绝不会携带同一引擎实例上一次运行的证据。

本页仅记录外部可观察的行为与受支持的公共 API 面向。内部命名空间路径、辅助类、机制表、运行手册文件名与工单前缀均超出范围。