契约 / 签章
签章领域包含六项契约。它们定义如何生成 CMS 签章、应用 RFC 3161 时间戳、使用硬件密钥签章,并启用长期验证。Core 发布这些契约;Pro 和 Enterprise 版本提供生产环境实现。
composer require nextpdf/core:^3概念总览
标题为“概念总览”的章节PDF 数字签章是存储在签章字典中的 CMS SignedData 结构。Contents 条目存放 DER 编码结构。ByteRange 条目指明摘要覆盖的字节范围。摘要覆盖整个文件,但排除签章值本身 —— 见 ISO 32000-2 §12.8.1。CMS 结构遵循 RFC 5652 §5.1:依次包含版本、摘要算法、封装内容和签章者信息。
SignerInterface 是核心契约。它为一段字节范围生成 CMS SignedData、为签章值应用时间戳,并报告是否支持长期验证。它承载 ETSI EN 319 142 定义的内容,覆盖从 PAdES 基线等级 B-B 到 B-LTA 的各个级别。每提升一个级别,都会增加对应材料。B-B 承载基本签章。B-T 添加签章时间戳。B-LT 添加撤销数据。B-LTA 添加归档时间戳。
HsmSignerInterface 使用存放在硬件安全模块(HSM)内的密钥进行签章。私钥不会离开硬件边界。此契约以 DER 形式返回签章者证书和证书链,供 CMS 层构建结构。DeferredSignerInterface 扩展 SignerInterface,用于支持异步签章。调用方提交数据、取得一个工作标识符、轮询完成状态,然后取回结果。当远程 HSM 或云密钥服务无法立即返回结果时,可以使用它。
TimestampProviderInterface 会请求一个 RFC 3161 时间戳令牌。该协议通过与时间戳机构(TSA)交换请求和响应来工作 —— 见 RFC 3161 §2.4。令牌中的 messageImprint 是签章值的哈希 —— RFC 3161 §2.4.2。LtvManagerInterface 用于启用长期验证。它会收集证书链、获取 OCSP 和 CRL 响应、构建文档安全存储区,并为 B-LTA 添加文档时间戳。CryptoPolicyInterface 会在任何密码学运算运行之前,控制允许使用的哈希、签章和加密算法,以及允许的密钥强度。
API 接口
标题为“API 接口”的章节| 类型 | 类别 | 主要成员 | 稳定性 | 起始版本 |
|---|---|---|---|---|
SignerInterface | 接口 | sign(string): SignatureResult、timestamp(string): string、supportsLtv(): bool | 稳定 | 1.0.0 |
HsmSignerInterface | 接口 | sign(string, string): string、getCertificateDer()、getCertificateChainDer()、getPublicKeyAlgorithm() | 稳定 | 1.0.0 |
DeferredSignerInterface | 接口 | submitForSigning(string): string、retrieveSignature(string)、isComplete(string)(扩充 SignerInterface) | 实验性 | 3.0.0 |
TimestampProviderInterface | 接口 | getTimestamp(string): string | 实验性 | 3.0.0 |
LtvManagerInterface | 接口 | enableLtv(...)、addDocumentTimestamp(...) | 稳定 | 1.10.0 |
CryptoPolicyInterface | 接口 | isHashAlgorithmAllowed()、isSignatureAlgorithmAllowed()、isEncryptionAlgorithmAllowed()、isKeyStrengthAllowed()、getPreferredHashAlgorithm()、getName() | 稳定 | 1.9.0 |
SignerInterface::sign() 会返回一个 NextPDF\Security\Signature\SignatureResult。公开的 $cmsSignedData 属性存放 DER 编码的 CMS。公开的 $digestHex 属性存放 SHA-256 摘要。公开的 $timestampToken 属性存放可选的 TSA 令牌。访问方法包括 toHex() / toHexPadded(int) / getSize() / hasTimestamp()。HsmSignerInterface::sign() 对 RSA 返回 DER 编码字节,对 ECDSA 返回原始 r‖s 字节。算法参数使用 OpenSSL 标识符。
代码范例 —— 快速入门
标题为“代码范例 —— 快速入门”的章节<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\SignerInterface;
/** * Sign a byte range with any SignerInterface implementation. * * @param SignerInterface $signer A core or Premium signer. * @param string $byteRange The PDF byte range to sign. * * @return string Hex-encoded CMS SignedData for the PDF /Contents field. */function signByteRange(SignerInterface $signer, string $byteRange): string{ $result = $signer->sign($byteRange);
return $result->toHex();}SignerInterface::sign() 会返回一个 SignatureResult。toHex() 会生成写入器放入 /Contents 字段的十六进制字符串;原始 DER 字节则保存在公开的 $cmsSignedData 属性上。此函数依赖的是契约,而不是具体类。无论是 Core 的测试签章器,还是 Premium 的 HSM 签章器,都能满足这个契约。
代码范例 —— 正式环境
标题为“代码范例 —— 正式环境”的章节<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\CryptoPolicyInterface;use NextPDF\Contracts\SignerInterface;use NextPDF\Contracts\TimestampProviderInterface;use NextPDF\Exception\NextPdfException;use Psr\Log\LoggerInterface;
final readonly class TimestampedSigningService{ public function __construct( private SignerInterface $signer, private TimestampProviderInterface $timestamps, private CryptoPolicyInterface $policy, private LoggerInterface $logger, ) {}
/** * Sign a byte range, then timestamp the CMS structure. * * @param string $byteRange The PDF byte range to sign. * * @return array{cms: string, digest: string, tst: string} */ public function sign(string $byteRange): array { if (!$this->policy->isHashAlgorithmAllowed($this->policy->getPreferredHashAlgorithm())) { throw new \LogicException('Preferred hash rejected by crypto policy.'); }
try { $signature = $this->signer->sign($byteRange); $token = $this->timestamps->getTimestamp($signature->cmsSignedData);
return [ 'cms' => $signature->toHex(), 'digest' => $signature->digestHex, 'tst' => $token, ]; } catch (NextPdfException $e) { $this->logger->error('Signing failed', [ 'policy' => $this->policy->getName(), 'error' => $e->getMessage(), ]);
throw $e; } }}此服务注入三项契约。运行签章运算之前,会先检查密码学原则。SignatureResult 通过公开的 $cmsSignedData 属性暴露 CMS 字节,通过 $digestHex 暴露 SHA-256 摘要,并使用 toHex() 生成 /Contents 十六进制字符串。时间戳提供者接收 CMS 字节,并返回一个 DER 编码令牌。catch 块会记录原则名称并重新抛出异常。该服务绝不会吞掉失败。
边界情况与陷阱
标题为“边界情况与陷阱”的章节- 字节范围摘要必须排除签章值。覆盖
Contents条目的摘要会生成永远无法验证的签章 —— ISO 32000-2 §12.8.1。 SignerInterface::supportsLtv()反映的是能力,而不是状态。签章器可以支持长期验证,但未配置时间戳服务时,仍会生成 B-B 签章。DeferredSignerInterface::retrieveSignature()在工作完成之前始终返回null。请先轮询isComplete(),避免每次检查都传输负载数据。结果一旦存在,取回操作就是幂等的。LtvManagerInterface::addDocumentTimestamp()必须在文档安全存储区写入之后才运行。若先调用它,会生成无效的 B-LTA 结构。CryptoPolicyInterface在未配置任何原则时,会对每个算法返回true。在受监管环境中,请配置明确的原则;不要依赖这个开放的默认值。HsmSignerInterface::getCertificateChainDer()不包含签章者证书。请使用getCertificateDer()获取签章者叶证书,并使用链方法获取中间证书。
签章成本主要取决于密码学运算和网络往返,而不是契约本身。本机软件签章通常为个位数毫秒。HSM 签章会增加设备往返时间。时间戳会增加一次到时间戳机构的网络往返。长期验证会为链中的每张证书增加一次 OCSP 或 CRL 获取。1500 毫秒墙钟时间的 performance_budget 覆盖连接已预热时、使用远程 TSA 的单个带时间戳签章。针对缓慢撤销端点的长期验证会超出此预算,应在请求路径之外运行。可重现性配置文件是 structural,而非 bitwise。时间戳会嵌入签章发生的瞬间时刻,因此两次运行的时间戳字节会有差异,而文档结构会保持相同。
安全注意事项
标题为“安全注意事项”的章节签章契约是引擎主要的密码学边界,因此威胁模型很明确。密钥保管是第一项考量:HsmSignerInterface 将私钥保留在硬件边界内,且此契约绝不会暴露密钥材料。算法降级是第二项考量:CryptoPolicyInterface 会在运算之前阻止弱哈希和过短密钥,使部署无需分支引擎即可强制应用 FIPS 140-3 或 eIDAS 配置文件。时间戳信任是第三项考量:RFC 3161 令牌的可信度仅限于其时间戳机构,因此提供者契约可注入,使部署能够固定选用自己的时间戳机构。长期验证是第四项考量:撤销材料会在签章时获取并存储在文档安全存储区,使验证在证书到期后仍能维持。请将每一笔签章器输入都视为不可信。字节范围由引擎计算,绝不接受调用方提供的字节范围。由于这些契约掌管密码学签章,本页标示为 export_control_class: legal-review-required。按照引用卫生原则,本文释义所有规范性来源,并未引述其原文。
符合性
标题为“符合性”的章节| 主张 | 标准 | 条款 | 证据 |
|---|---|---|---|
签章值以 DER 编码存储在签章字典的 Contents 条目中,形式为 CMS SignedData 或 TimeStampToken。 | ISO 32000-2 | §12.8.1 | |
摘要针对 ByteRange 数组定义的字节范围计算,并排除签章值。 | ISO 32000-2 | §12.8.1 | , |
| 长期验证材料承载于文档安全存储区,其中包含 VRI、OCSP、CRL 和证书条目。 | ISO 32000-2 | §12.8.4.3 | , |
| PAdES 长期验证会将验证数据放入 DSS 和 VRI 字典中。 | ETSI EN 319 142-2 | §6.3 | , |
RFC 3161 时间戳令牌通过 messageImprint 绑定签章值的哈希,并通过 TSA 请求和响应进行交换。 | RFC 3161 | §2.4.2、§2.4 | , |
| CMS SignedData 序列承载版本、摘要算法、封装内容和签章者信息。 | RFC 5652 | §5.1 |
所有条款均为释义。NextPDF 不会重制规范性文本。如需权威措辞,请查阅已发布的标准。
商业脉络
标题为“商业脉络”的章节Core 发布并冻结签章契约。HsmSignerInterface、LtvManagerInterface 以及延迟签章器背后的生产环境实现,会随 Pro 和 Enterprise 版本一同提供,包含 PKCS#11 硬件集成以及 PAdES B-LT 与 B-LTA。Core 会在运行阶段通过 class_exists() resolve(解析)这些实现并转换为契约,因此开源引擎不带任何商业依赖,且 API 在升级时不会改变。