安全 / 签章:CMS、RFC 3161 时间戳记、LTV 与信任
本页说明 NextPDF Core 提供的签章接口:生成一份 CMS 签章、应用 RFC 3161 时间戳记、按 RFC 5280 验证证书链,并通过 OCSP 与 CRL 检查撤销状态。内容聚焦于行为层面。Core 的实现类属于内部实现——生产环境代码应依赖 SignerInterface 这份契约,而不是具体的 NextPDF\Security\Signature 类型。生成的签章是否能通过验证,由验证端及其设定的信任锚点决定。该结果不由生成端控制,本页会在每个相关位置明确说明这一点。
composer require nextpdf/core:^3概念总览
标题为“概念总览”的章节Core 会从字节范围构建出一份 CMS SignedData 结构,再以 DER 编码存入签章字典的 Contents 条目——ISO 32000-2 §12.8.1。该结构带有 SignerInfo 的已签署属性——其中包含 content-type 与 message-digest——RFC 5652 §5.3。验证端会重新计算内容摘要,并与 message-digest 属性比对。两者必须相符,签章才算有效——RFC 5652 §5.4。SignerInfo 也会带有摘要算法标识符与已签署属性区块——RFC 5652 §5。对于 Core 提供的软件签章路径,它会通过 phpseclib3 进行签章:RSA、RSASSA-PSS、ECDSA 与 Ed25519。
一次 RFC 3161 时间戳记流程,是与时间戳记机构(Time-Stamping Authority)之间的一次请求/响应往返,机构会返回一份 TSTInfo 结构——RFC 3161 §2.4.1。每个令牌都带有一个对签发它的 TSA 而言唯一的 serialNumber——RFC 3161 §2.4.2——以及一个以 UTC 表示、代表令牌创建时刻的 genTime——RFC 3161 §2.4.2。
信任验证包含两项检查。路径验证会从签署者证书一路走访到信任锚点,沿途检查基本约束与路径构建输入——RFC 5280 §6.1。撤销检查会向 OCSP 响应方查询,或读取一份 CRL:一份 OCSP 响应会报告 good、revoked 或 unknown——RFC 6960 §2.2——而响应的 thisUpdate 与 nextUpdate 则界定了该状态的新鲜度——RFC 6960 §4.2。信任锚点与撤销新鲜度策略都由调用方提供。引擎只根据收到的输入进行验证,并不内置信任清单。
API 接口
标题为“API 接口”的章节| 类型 | 类别 | 角色 | 稳定性 | 起始版本 |
|---|---|---|---|---|
SignerInterface | 接口(NextPDF\Contracts) | 调用方依赖的签章契约 | 稳定 | 1.0.0 |
SignatureLevel | enum(枚举) | PAdES 等级选择器与可用性检测 | 稳定 | 1.0.0 |
Rfc5280PathValidator | 接口 | 证书路径验证入口(validate(...)) | 稳定(自 3.1.0 起冻结) | 3.1.0 |
RevocationStatus | 枚举 | OCSP / CRL 结果:good、revoked、unknown | 稳定 | 3.1.0 |
CaTrustAnchorBundle | 类型 | 由调用方提供的一组信任锚点 | 稳定 | 3.1.0 |
TstInfo | 类型 | 已解析的 RFC 3161 时间戳记字段 | 稳定 | 3.2.0 |
SignerInterface::sign() 会返回一个 SignatureResult,其 toHex() 会生成 /Contents 的十六进制字符串,而其 cmsSignedData 属性则持有原始 DER 字节。实现这些行为的具体 NextPDF\Security\Signature 类属于内部实现(在模块清单中标为 stability: internal);它们并非公开 API 的一部分,可能在不提升主版本号的情况下变更。请依赖上述契约和枚举。
代码范例——快速上手
标题为“代码范例——快速上手”的章节<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\SignerInterface;
/** * Produce the CMS SignedData hex for a PDF /Contents field. * * @param SignerInterface $signer A Core or Premium signer. * @param string $byteRange The PDF byte range to sign. * * @return string Hex-encoded CMS SignedData. */function sign(SignerInterface $signer, string $byteRange): string{ return $signer->sign($byteRange)->toHex();}调用方依赖的是契约。Core 的软件签章器与 Premium 的 HSM 签章器都满足 SignerInterface,因此这段代码在各版本之间保持不变。
代码范例——正式环境
标题为“代码范例——正式环境”的章节<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\SignerInterface;use NextPDF\Contracts\TimestampProviderInterface;use NextPDF\Exception\NextPdfException;use Psr\Log\LoggerInterface;
final readonly class TimestampedSigner{ public function __construct( private SignerInterface $signer, private TimestampProviderInterface $timestamps, private LoggerInterface $logger, ) {}
/** * Sign a byte range, then timestamp the CMS structure. * * The timestamp's trust still depends on the verifier accepting the * Time-Stamping Authority; this method only produces the structure. * * @param string $byteRange The PDF byte range to sign. * * @return array{cms: string, tst: string} */ public function sign(string $byteRange): array { try { $signature = $this->signer->sign($byteRange); $token = $this->timestamps->getTimestamp($signature->cmsSignedData);
return ['cms' => $signature->toHex(), 'tst' => $token]; } catch (NextPdfException $e) { $this->logger->error('Signing failed', ['error' => $e->getMessage()]);
throw $e; } }}时间戳记提供者通过注入提供,使每个部署环境都能固定选用自己的时间戳记机构(Time-Stamping Authority)。catch 代码块会记录并重新抛出异常。它绝不吞掉失败,让签章路径保持 fail-closed(失败即关闭)。
边界情况与陷阱
标题为“边界情况与陷阱”的章节- 已生成的签章并不等于已验证的签章。路径验证与撤销检查是在验证端、基于该验证端的信任锚点执行。生成端无法断定其结果。
- 字节范围摘要会排除签章值本身。包含
Contents八位元组的摘要无法通过验证——ISO 32000-2 §12.8.1。 - 撤销状态存在一个新鲜度时间窗。一份 OCSP 响应的有效期只覆盖它的
thisUpdate/nextUpdate区间——RFC 6960 §4.2。陈旧的响应无法取代验证当下的一次实时检查。 - 引擎不内置信任清单。
CaTrustAnchorBundle由调用方提供;空的 bundle(包)按设计表示没有任何证书链能通过验证。 - OCSP 的
unknown并不等于good。必须把unknown视为无法判定,而不是隐含通过——RFC 6960 §2.2。 - HSM 密钥保管、延后签章与云端签章,以及 PAdES B-LT / B-LTA 生成器,都不在 Core 之中。在 Core 分发版本中选择这些路径时,会 fail closed(失败即关闭),并以一条消息指出缺失的 Enterprise 组件名称。
一次软件签章只需个位数毫秒。加上时间戳记则会多一次到 TSA 的网络往返。证书一旦加载到内存,路径验证就是本地操作;撤销检查则会为链中每张证书各增加一次 OCSP 或 CRL 获取。1500 毫秒的整体预算涵盖的是在连接已预热的情况下、向远程 TSA 取得时间戳记的单次签章。对响应缓慢的端点进行撤销检查会超出此预算,应移到请求路径之外处理。可复现性配置文件为 structural:时间戳记会嵌入签署瞬间,因此两次执行的时间戳记字节会不同,但文件结构完全相同。
安全性注意事项
标题为“安全性注意事项”的章节这是引擎主要的密码学边界,因此威胁模型在此明确说明。字节范围由引擎计算,绝不接受调用方提供的值。签章路径为 fail-closed:密码学原语失败或能力缺口都会抛出带类型的异常,绝不会静默降级为较弱算法。在要求时间戳记的等级(B-T、B-LT、B-LTA)下,如果时间戳记机构(Time-Stamping Authority)返回空令牌,即构成终止性故障:签章会被拒绝,而不会以静默未加时间戳、已降级的状态产出,除非已接入故障处理程序来授权一次有文档记录的降级。按设计,信任由调用方掌控——锚点与撤销策略都是输入,而不是引擎默认值——因为如果由生成端判定自身可信性,就等于判定一个只有验证端才能确立的事实。时间戳记的信任取决于对时间戳记机构(Time-Stamping Authority)的信任,而后者可注入,使每个部署环境都能固定选用自己的机构。本页被标记为 export_control_class: legal-review-required,因为它涉及加密签章;按照引用规则,每一项规范性来源都已改述,没有重制任何原文。
符合性
标题为“符合性”的章节| 主张 | 标准 | 条款 | 佐证 |
|---|---|---|---|
CMS 签章以 DER 编码存入签章字典的 Contents 条目。 | ISO 32000-2 | §12.8.1 | |
| SignerInfo 带有 content-type 与 message-digest 已签署属性。 | RFC 5652 | §5.3 | |
| 验证端会重新计算内容摘要,并与 message-digest 属性比对。 | RFC 5652 | §5.4 | |
| 时间戳记令牌由 RFC 3161 TSA 产生,并带有唯一的 serialNumber 与一个 UTC 的 genTime。 | RFC 3161 | §2.4.1, §2.4.2 | ,, |
| 证书路径验证会检查基本约束,以及从签署者到信任锚点的路径输入。 | RFC 5280 | §6.1 | , |
| OCSP 会报告 certStatus 为 good、revoked 或 unknown,并以 thisUpdate / nextUpdate 为界。 | RFC 6960 | §2.2, §4.2 | , |
所有条款均以改述方式呈现。NextPDF 不重制规范性原文。权威表述请参阅已发布的标准。
商业上下文
标题为“商业上下文”的章节Core 提供软件 CMS 签章器(RSA、RSASSA-PSS、ECDSA、Ed25519)、RFC 3161 时间戳记消费能力、RFC 5280 路径验证,以及 OCSP / CRL 撤销检查。HSM 与 PKCS#11 密钥保管、延后签章与云端签章、PAdES B-LT 与 B-LTA 生成器,以及 FIPS 140-3 密码学策略配置文件,则由 Pro 与 Enterprise 版本提供。Core 会在运行时按契约 resolve(解析)这些组件,因此这套开源引擎不带任何商业依赖,升级时 API 也不会改变。
另请参阅
标题为“另请参阅”的章节- PAdES 基准对应——对比 Core 与 Premium 在 B-B、B-T、B-LT、B-LTA 上的支持。
- 契约 / 签章——
SignerInterfaceSPI 与稳定性层级。 - 安全性——加密以及更广泛的签章接口。
- 符合性——与已签署归档配套的配置文件强制执行。
- CMS · RFC 3161 时间戳记 · LTV · DSS · VRI · HSM——词汇表术语。