故障排查:签章与时间戳失败
适用范围
标题为“适用范围”的章节这些条目涵盖引擎通过 NextPDF\Exception\SignatureException 与 NextPDF\Security\Signature\Exception\SignatureLevelUnreachableException 抛出的签章失败。每则条目都会指明确切的工厂方法或类别,便于你根据消息与 getContext() 确认原因,而不是靠猜测。
关于措辞的提醒:引擎并不证明签章有效,也不证明文件受到保护。它只报告检测到的失败。请把每则解法都视为「移除一个已报告原因」的步骤。
条目:无法生成 PAdES 的「B-LT」或「B-LTA」层级
标题为“条目:无法生成 PAdES 的「B-LT」或「B-LTA」层级”的章节- 症状。
SignatureException,消息结尾为nextpdf/enterprise package is required for B-LT/B-LTA signatures。 - 可能原因。 缺少长期验证(long-term-validation)能力提供者。B-LT 与 B-LTA 会嵌入撤销数据与归档时间戳;该提供者随
nextpdf/enterprise一同提供。 - 证据/诊断。 工厂方法
SignatureException::ltvCapabilityMissing()会产生这则确切消息。getContext()返回的signature_level会设为你尝试使用的层级。 - 解法。
- 安装提供者:执行
composer require nextpdf/enterprise。 - 重新执行签署调用。
- 如果你无法安装该提供者,请改为请求核心包可生成的
B-B或B-T。
- 安装提供者:执行
- 相关连结。 例外状况参考。
条目:签章层级无法达到,调用遭拒
标题为“条目:签章层级无法达到,调用遭拒”的章节- 症状。
SignatureLevelUnreachableException,消息格式为PAdES level "<x>" is unreachable (highest achievable: "<y>")。 - 可能原因。 你请求的合规层级需要签署时尚不可用的基础设施——最常见的是 B-T 以上层级所需的时间戳授权机构。引擎采用失败即关闭(fail-closed)做法:它不会悄悄降级后仍对外宣称达到较高层级。
- 证据/诊断。
getContext()会返回requestedLevel、highestAchievableLevel与reason。reason字段会说明基础设施的缺口所在。这是默认的失败即关闭行为,用来防止文件宣称一个它其实未达到的层级。 - 解法。
- 阅读
reason字段,找出缺少的基础设施。 - 补上缺少的组件——例如配置一个时间戳授权机构——然后重新执行调用。
- 如果要刻意接受较低的层级,请把
allowDegradation: true传给PadesOrchestrator。随后调用会生成highestAchievableLevel,并报告它实际生成的层级。
- 阅读
- 相关连结。 加密与权限。
条目:需要时间戳授权机构客户端,却未提供
标题为“条目:需要时间戳授权机构客户端,却未提供”的章节- 症状。
SignatureException,结尾为TSA client is required for level <x> but none was provided。 - 可能原因。 B-T、B-LT 或 B-LTA 的请求需要时间戳授权机构客户端,但协调器(orchestrator)上没有接入任何客户端。
- 证据/诊断。 工厂方法
SignatureException::tsaRequired()会产生这则消息;getContext()会包含你尝试使用的signature_level。 - 解法。
- 配置一个时间戳授权机构客户端,并把它传给协调器。
- 重新执行调用。
- 如果要生成不需要时间戳的层级,请请求
B-B。
- 相关连结。 例外状况参考。
条目:时间戳授权机构的端点 URL 为空
标题为“条目:时间戳授权机构的端点 URL 为空”的章节- 症状。
SignatureException,结尾为TSA endpoint URL is empty。 - 可能原因。 构造时间戳授权机构客户端时,提供的端点 URL 为空。
- 证据/诊断。 工厂方法
SignatureException::tsaUrlEmpty()会产生这则消息。这是配置缺陷,而不是网络失败。 - 解法。
- 在时间戳授权机构客户端上配置一个非空的端点 URL,例如
https://timestamp.example.com/tsa。 - 如果你请求的层级并不需要时间戳,请改为移除时间戳授权机构客户端的接入。
- 重新执行调用。
- 在时间戳授权机构客户端上配置一个非空的端点 URL,例如
- 相关连结。 例外状况参考。
条目:缓冲区中缺少签章 placeholder(占位符)
标题为“条目:缓冲区中缺少签章 placeholder(占位符)”的章节- 症状。
SignatureException,结尾为no /Contents <…> field found in PDF buffer (signature placeholder missing)。 - 可能原因。 签署阶段在未预留签章容器的缓冲区上执行,因此没有位置可写入签章。
- 证据/诊断。 工厂方法
SignatureException::signatureContentsNotFound()会产生这则消息。 - 解法。
- 请确保签章字段及其占位符在签署阶段执行之前就已写入。
- 重新执行流水线,让占位符在签署开始时已存在。
- 相关连结。 例外状况参考。
条目:撤销状态未知(OCSP 响应者拒绝处理)
标题为“条目:撤销状态未知(OCSP 响应者拒绝处理)”的章节- 症状。
SignatureException,结尾为OCSP responder returned non-successful OCSPResponseStatus "<status>"。 - 可能原因。 OCSP 响应者未返回
successful状态,因此未产生任何撤销主张。引擎遵循 RFC 6960 §4.2.1(源代码中也引用了该规范):只有successful (0)状态才允许响应主体包含内容。引擎拒绝把遭拒绝处理的响应当成「信任为真」的结果。 - 证据/诊断。 工厂方法
SignatureException::nonSuccessfulOcspResponseStatus()会产生这则消息,并点明所报告的状态,例如tryLater或internalError。如果是保留或未知的状态字节,则改为产生SignatureException::reservedOcspResponseStatus()。 - 解法。
- 从消息中识别出该状态。如果是
tryLater这类暂时性状态,请稍后重试获取撤销数据。 - 如果是
unauthorized或malformedRequest,请检查 OCSP 请求的 URL,以及响应者预期的证书。 - 不要为了取得 B-LT 或 B-LTA 产出物而压制此失败;撤销主张是该层级的一部分。
- 从消息中识别出该状态。如果是
- 相关连结。 例外状况参考。
条目:证书链项目无法解码
标题为“条目:证书链项目无法解码”的章节- 症状。
SignatureException,结尾为failed to base64-decode PEM body — input is not valid PEM。 - 可能原因。 某个证书链项目不是有效的 PEM——通常是被截断、夹杂了多余字符,或是在预期 PEM 的位置提供了二进制 DER 区块。
- 证据/诊断。 工厂方法
SignatureException::pemDecodingFailed()会在组装证书链时产生这则消息。 - 解法。
- 逐一检查链上的每张证书是否包含多余字符或遭到截断。
- 以 PEM 格式重新导出受影响的证书。
- 重新执行签署调用。
- 相关连结。 加密与权限。
条目:私钥类型与算法不匹配
标题为“条目:私钥类型与算法不匹配”的章节- 症状。
SignatureException,结尾为expected private key of type "<x>" for the configured algorithm but got "<y>"。 - 可能原因。 加载的私钥与所配置的签章算法不匹配——例如用了 RSA 密钥却选择了 ECDSA。
- 证据/诊断。 工厂方法
SignatureException::unexpectedKeyType()会产生这则消息,并同时点明预期与实际的密钥类型。 - 解法。
- 确认证书与密钥对和你选定的算法相匹配。
- 请将算法选择改为与密钥相匹配,或加载与算法相匹配的密钥。
- 重新执行签署调用。
- 相关连结。 例外状况参考。
条目:Ed25519 密钥或签章数据格式错误
标题为“条目:Ed25519 密钥或签章数据格式错误”的章节- 症状。
SignatureException,结尾会指出 Ed25519 长度不符——例如Ed25519 signature length <n> ≠ expected 64 bytes,或Ed25519 round-trip self-verify failed。 - 可能原因。 运行时的密码学组件返回了长度错误的密钥或签章数据,或刚生成的签章无法用自己的公钥验证。引擎在源代码中引用 RFC 8032 §3.4,该节规定分离式 Ed25519 签章固定为 64 个字节。引擎宁愿中止,也不会发出无法自我验证的数据。
- 证据/诊断。 相关的工厂方法为
SignatureException::ed25519SignatureMalformed()、::ed25519RoundTripVerifyFailed()、::ed25519KeyParseFailed()、::ed25519SeedInvalid()、::ed25519SecretKeyMalformed(),以及::ed25519PublicKeyInvalid()。每一个都会点明观测到的长度。 - 解法。
- 重新安装 libsodium PHP 扩展;文档中已记载,被精简或损坏的构建正是长度错误数据的原因。
- 确认该密钥是 Ed25519 密钥,且 OpenSSL 为 1.1.1 或更新版本。
- 重新执行签署调用。
- 相关连结。 例外状况参考。
条目:未发出归档时间戳字典
标题为“条目:未发出归档时间戳字典”的章节- 症状。
SignatureException,结尾为no /Type /DocTimeStamp dictionary was emitted into the PDF buffer。 - 可能原因。 B-LTA 归档循环虽已执行,文件时间戳字典却从未写入缓冲区,因此产出物会是一份只完成一半的 B-LTA。引擎会拒绝返回它。
- 证据/诊断。 工厂方法
SignatureException::documentTimestampNotEmitted()会产生这则消息。这是在最终化阶段抛出的后置条件失败。 - 解法。
- 请把输出视为已丢弃;不要交付这份不完整的产出物。
- 使用一个可连接的时间戳授权机构,重新执行 B-LTA 流水线。
- 如果失败反复出现,请获取
getContext()与链上的前一个异常,作为缺陷报告材料。
- 相关连结。 例外状况参考。
边界案例与陷阱
标题为“边界案例与陷阱”的章节- 这些工厂方法只有在信息可用时,才会把
cert_info设为主体名称或指纹;对于能力失败与配置失败,空的cert_info属于正常预期。 - 未配置 HTTP 客户端的 B-LT 或 B-LTA 请求会抛出
SignatureException::httpClientMissing()——获取撤销数据需要一个传给协调器的 PSR-18 客户端。 - 没有签署器实现的 HSM 托管凭证会抛出
SignatureException::hsmSignerMissing();请在签署前把签署器接入凭证。