EPdfKeychainError = class(Exception);
TPdfKeychainSignerProvider = class(TInterfacedObject, IPadesSignerProvider)
constructor Create(const KeychainPath: string = '');
function BackendName: string;
function Resolve(const Options: TPadesSignerOptions): TPadesResolvedSigner;
end;
TPdfKeychainCmsVerifier = class(TInterfacedObject, IPdfCmsVerifier)
function BackendName: string;
function VerifyDetached(const CmsDer: TBytes; Segment1: Pointer; Segment1Length: Cardinal; Segment2: Pointer; Segment2Length: Cardinal; const Options: TPdfCmsVerifyOptions): TPdfCmsVerifyResult;
function VerifyAttached(const CmsDer: TBytes; const Options: TPdfCmsVerifyOptions): TPdfCmsVerifyResult;
end;
function KeychainAvailable: Boolean;
function KeychainMissingSymbols: string;
function KeychainVerificationAvailable: Boolean;
function ConfigureKeychainSignerProvider: IPadesSignerProvider;
function ConfigureKeychainCmsVerifier: IPdfCmsVerifier;
procedure ConfigureKeychainTrustAnchors(const Anchors: TPadesChainCertArray);
procedure ConfigureKeychainOcspResponses(const Responses: TPadesChainCertArray);
FPdfCryptoMac 是 FPdfCrypto 和 FPdfCms 使用的 macOS 加密后端,它覆盖平台集成的两个部分:通过 Keychain 身份进行 PAdES 签名,以及通过系统 CMS 解码器和信任评估进行 CMS 验证。Security.framework 和 CoreFoundation 在运行时按名称加载并解析符号,而不是链接,因此本单元可以在所有目标平台上编译,并在运行时报告平台服务是否真正可达
在签名一侧,TPdfKeychainSignerProvider 实现 IPadesSignerProvider。它的 Resolve 方法枚举 Keychain 身份,选出证书与 TPadesSignerOptions 携带的 SHA-1 指纹匹配的那一个,读取证书 DER 与对应的私钥引用,并返回 TPadesResolvedSigner,其 ICmsSigner 通过 Security.framework 对 CMS 已签名属性进行签名,因此私钥从不离开 Keychain。选项中请求的填充方案会与密钥实际支持的能力相调和,RSASSA-PSS 参数与其他后端生成的完全相同,因此在 macOS 上签名的文档携带一致的 AlgorithmIdentifier。ConfigureKeychainSignerProvider 构建该提供方并将其安装为活动的 PAdES 签名后端,返回它注册的接口
验证侧的 TPdfKeychainCmsVerifier 基于 CMSDecoder 和 SecTrust 实现 IPdfCmsVerifier。VerifyDetached 将签名空档两侧的两个已签名字节范围拼成解码器要求的分离内容,再验证 CMS;VerifyAttached 验证自带内容的 CMS,时间戳 token 就属于这种形式。两者都返回 TPdfCmsVerifyResult,分别报告签名、信任和吊销状态,可按需收集签名者证书与评估出的证书链,并遵守 TPdfCmsVerifyOptions 中的吊销、在线获取和 checkpoint 设置。ConfigureKeychainCmsVerifier 创建验证器并将其注册为当前 CMS 验证后端
签名有效性与证书信任保持为相互独立的结果,因此不受信任或自签链下的真实签名仍然可与被篡改的内容区分开
对于内嵌的 RFC 3161 token,带上限的 DER 解析会把原始封装的 TSTInfo 字节交给原生解码器,不修改其内容类型或已签名属性。普通 CMS 验证会从原生输入中剔除未签名属性,避免自动时间戳处理替代外层签名自身的完整性或证书链结果。这次验证不检查 countersignature 或其他未签名属性;ValidatePadesTrust 会单独验证各原始时间戳 token、message imprint、TSA 证书角色、信任和吊销结果
向 TPdfKeychainSignerProvider.Create 传入 Keychain 文件路径,可通过仅用于本次查询的搜索列表,将身份查找限定到该 Keychain,再使用 ConfigurePadesSignerProvider 注册提供器。空路径使用当前用户搜索范围。打开或搜索所选 Keychain 不会修改默认 Keychain、用户搜索列表或系统信任配置;无效路径或限定搜索所需符号不可用会抛出 EPdfKeychainError
Resolve 还会通过离线、仅限本次查询的证书查找和原生证书链构建,导出签名证书可取得的签发者链。候选证书最多 4096 个,每个 1 MiB,总计 16 MiB;构建的链最多 32 个证书,包含叶证书,而叶证书与 ChainCertDer 分开返回。导出证书材料不表示信任成立,也不会安装信任锚;无法取得的签发者不能凭空补出
| 函数 | 说明 |
|---|---|
KeychainAvailable | 当 Security.framework 和 CoreFoundation 已加载且签名所需的全部符号解析成功时返回 True |
KeychainMissingSymbols | 返回解析失败的名称(以空格分隔),全部绑定成功时返回空字符串 |
ConfigureKeychainSignerProvider | 构建 TPdfKeychainSignerProvider,将其安装为 PAdES 签名后端,并作为 IPadesSignerProvider 返回 |
TPdfKeychainSignerProvider.Create | 可选地指定已有 Keychain 文件来限定身份查找范围,拒绝内嵌 NUL 字符和超过 4096 个字符串代码单元的路径 |
TPdfKeychainSignerProvider.BackendName | 返回签名提供方所报告的后端标识符 |
TPdfKeychainSignerProvider.Resolve | 将 TPadesSignerOptions 与 Keychain 身份进行匹配,返回持有签名器、证书 DER 与链的 TPadesResolvedSigner |
| 函数 | 说明 |
|---|---|
KeychainVerificationAvailable | 当 CMS 解码器、信任策略与信任评估符号解析成功时返回 True |
ConfigureKeychainCmsVerifier | 构建 TPdfKeychainCmsVerifier,将其安装为 CMS 验证后端,并作为 IPdfCmsVerifier 返回 |
ConfigureKeychainTrustAnchors | 克隆显式 DER 信任锚,供之后每次信任评估补充系统信任锚,不修改 Keychain;上限为 64 个证书、每证书 4 MiB、总计 16 MiB,空数组清除配置 |
ConfigureKeychainOcspResponses | 克隆显式 DER OCSP 证据,供原生 Security.framework 评估;上限为 8 个响应、每响应 1 MiB、总计 4 MiB,空数组清除配置 |
TPdfKeychainCmsVerifier.BackendName | 返回验证后端所报告的后端标识符 |
TPdfKeychainCmsVerifier.VerifyDetached | 使用给定的 TPdfCmsVerifyOptions,针对由 Segment1、Segment1Length、Segment2 和 Segment2Length 描述的两个调用方内容段验证分离的 CMS 值 |
TPdfKeychainCmsVerifier.VerifyAttached | 使用给定的 TPdfCmsVerifyOptions,验证携带自身已签名内容的附着 CMS 值 |
| 类型 | 成员与用途 |
|---|---|
EPdfKeychainError | 当 Security.framework 不可用、没有 Keychain 身份与请求的签名器匹配,或平台签名调用失败时抛出的异常 |
TPdfKeychainSignerProvider | 实现 IPadesSignerProvider 的 PAdES 签名后端,包含 BackendName 与 Resolve |
TPdfKeychainCmsVerifier | 实现 IPdfCmsVerifier 的 CMS 验证后端,包含 BackendName、VerifyDetached 与 VerifyAttached |
KeychainAvailable 与 KeychainVerificationAvailable 分别报告签名与验证,因为两部分需要不同的框架符号,系统可能绑定其中一个而未绑定另一个KeychainMissingSymbols 列出每一个解析失败的符号,据此可以区分框架缺失与绑定问题:结果为空表示全部绑定成功;结果中出现框架路径表示框架没有加载;结果中出现具体入口点表示框架已加载但某个名称未解析KeychainAvailable 和 KeychainVerificationAvailable 返回 False,KeychainMissingSymbols 返回空字符串,配置函数抛出 EPdfKeychainErrorTPadesSignerOptions 未携带证书指纹时,将选择 Keychain 中第一个可用的身份,这适合测试而不适合生产签名pcvsUnsupported 并保留原生诊断,不把完整性判为有效或无效。此类值应使用 OpenSSL CMS 验证器;混合签名者诊断也保留各签名者独立的算法结果FreshOcspResponse 时间和 DER 预过滤,再由原生后端检查响应者签名、权限及证书。允许偏差为 300 秒,producedAt 和 thisUpdate 距当前时间最多一天OnlineRetrieval=False 禁用原生信任网络检索。原生 Security.framework 在线评估遵循平台行为;本后端的 UrlRetrievalTimeoutMs 不提供 OpenSSL 那种受限传输时限CollectSignerDiagnostics=True 返回独立签名者快照,受 MaxSignerCount 和硬上限 64 限制。返回证书材料限制为每证书 4 MiB、总计 64 MiB;收集不完整时报告 pcvsIndeterminateSignerDiagnosticsStatus=pcvsValid 表示收集完整,包括失败签名者的判定。RevocationError 携带原生 Security.framework 状态,本后端的 RevocationCertificate 保持为空