跳转到内容

安全 / 签章:CMS、RFC 3161 时间戳记、LTV 与信任

本页说明 NextPDF Core 提供的签章接口:生成一份 CMS 签章、应用 RFC 3161 时间戳记、按 RFC 5280 验证证书链,并通过 OCSP 与 CRL 检查撤销状态。内容聚焦于行为层面。Core 的实现类属于内部实现——生产环境代码应依赖 SignerInterface 这份契约,而不是具体的 NextPDF\Security\Signature 类型。生成的签章是否能通过验证,由验证端及其设定的信任锚点决定。该结果不由生成端控制,本页会在每个相关位置明确说明这一点。

Terminal window
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 响应会报告 goodrevokedunknown——RFC 6960 §2.2——而响应的 thisUpdatenextUpdate 则界定了该状态的新鲜度——RFC 6960 §4.2。信任锚点与撤销新鲜度策略都由调用方提供。引擎只根据收到的输入进行验证,并不内置信任清单。

类型类别角色稳定性起始版本
SignerInterface接口(NextPDF\Contracts调用方依赖的签章契约稳定1.0.0
SignatureLevelenum(枚举)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 的一部分,可能在不提升主版本号的情况下变更。请依赖上述契约和枚举。

examples/contracts/signing-quickstart.php
<?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,因此这段代码在各版本之间保持不变。

examples/contracts/signing-trust.php
<?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 也不会改变。