EPdfPkcs11Error = class(Exception);
TPdfPkcs11Options = record
ModulePath: string;
SlotId: UInt64;
UseSpecificSlot: Boolean;
Pin: string;
CertificateLabel: string;
class function Default: TPdfPkcs11Options; static;
end;
TPdfPkcs11SignerProvider = class(TInterfacedObject, IPadesSignerProvider,
IPdfPkcs11SignerProvider)
constructor Create(const Options: TPdfPkcs11Options);
destructor Destroy; override;
function BackendName: string;
function Resolve(const Options: TPadesSignerOptions): TPadesResolvedSigner;
function EnumerateSlots: TPdfPkcs11Slots;
function EnumerateCertificates(SlotID: UInt64): TPdfPkcs11Certificates;
procedure CloseProtectedSessions;
end;
function Pkcs11ModuleAvailable(const ModulePath: string): Boolean;
function Pkcs11ModuleVersion(const ModulePath: string): string;
function Pkcs11AbiLayout: string;
function ConfigurePkcs11SignerProvider(
const Options: TPdfPkcs11Options): IPdfPkcs11SignerProvider;
FPdfPkcs11 是可移植的 PAdES 签名后端,对应 Windows 上的 CNG 后端。它从 PKCS#11 令牌解析签名者证书及其私钥,然后返回 TPadesResolvedSigner,其 ICmsSigner 通过令牌生成 SignerInfo 签名,因此私钥从不离开令牌
模块在运行时按路径加载——opensc-pkcs11.so、libsofthsm2.so 或厂商 DLL——因此构建时不链接任何东西,模块缺失会被报告而不是致命错误。只有 C_GetFunctionList 按名称解析;其余入口点都来自它返回的函数表,这也正是标准设计期望的模块访问方式
ConfigurePkcs11SignerProvider 根据一个 TPdfPkcs11Options 值构建 TPdfPkcs11SignerProvider,并将其安装为活动的 PAdES 签名器后端,返回同时携带令牌枚举方法的 IPdfPkcs11SignerProvider 接口
Pkcs11ModuleAvailable 和 Pkcs11ModuleVersion 报告给定模块能否加载并暴露可用的函数表,应用因此可以在启动时判断本机能否进行令牌签名。已加载的模块会按路径进行 cache,因为标准允许每个进程对每个模块只初始化一次
Resolve 在提供了 TPadesSignerOptions.CertificateThumbprint 时按其选择证书,否则按 CertificateLabel 选择,然后查找与证书携带相同令牌标识符的私钥对象。证书密钥算法决定算法族:EC 密钥用 ECDSA 签名,其原始签名被重新编码为 CMS 期望的 DER SEQUENCE;RSA 密钥则遵循请求的填充方案
EnumerateSlots 和 EnumerateCertificates 服务于选择类用户界面,让应用在任何签名选项确定之前列出模块报告的槽位以及所选槽位中的证书
| Function | Description |
|---|---|
Pkcs11ModuleAvailable | 当给定路径的模块能加载并暴露可用的函数表时返回 True |
Pkcs11ModuleVersion | 返回模块报告的版本字符串,模块无法加载时返回空字符串 |
Pkcs11AbiLayout | 返回本构建解析出的 ABI 布局,形式为 ulong=<n> attr=<n> pss=<n> table=<n> |
ConfigurePkcs11SignerProvider | 根据给定选项构建提供方,将其安装为 PAdES 签名器后端,并作为 IPdfPkcs11SignerProvider 返回 |
| Type | Purpose |
|---|---|
EPdfPkcs11Error | 当未配置模块路径、模块无法加载、令牌调用失败,或找不到匹配的证书或私钥时抛出的异常 |
TPdfPkcs11Options | 记录提供方使用的模块、槽位、PIN 和证书 |
TPdfPkcs11SignerProvider | 在 PKCS#11 模块之上实现 IPadesSignerProvider 与 IPdfPkcs11SignerProvider 的提供方类 |
| Field | Description |
|---|---|
ModulePath | PKCS#11 模块的路径;必填,因为由哪个模块服务令牌属于部署决策,没有有用的默认值 |
SlotId | 要打开的令牌槽位,仅在 UseSpecificSlot 为 True 时生效 |
UseSpecificSlot | 保持为 False 即采用第一个报告令牌在位的槽位 |
Pin | 用户 PIN;令牌消费之后提供方会清除自己的副本,传入字符串的生命周期仍由调用方负责 |
CertificateLabel | 用于签名的证书的对象标签,可选,仅在 TPadesSignerOptions.CertificateThumbprint 为空时使用 |
Default | 静态工厂,返回模块路径为空、槽位为 0、自动选择槽位且无 PIN 与标签的选项 |
| Member | Description |
|---|---|
Create | 根据 TPdfPkcs11Options 值构造提供方;模块本身在首次使用时打开 |
Destroy | 在实例释放之前关闭所有打开的令牌会话 |
BackendName | 返回后端名称,配置了模块路径时会附加模块文件名 |
Resolve | 将 TPadesSignerOptions 解析为持有令牌签名器与签名者证书 DER 的 TPadesResolvedSigner |
EnumerateSlots | 以 TPdfPkcs11Slots 返回模块报告的所有槽位,包括没有令牌在位的槽位 |
EnumerateCertificates | 以 TPdfPkcs11Certificates 返回一个槽位中持有的证书,会先在该槽位上打开会话 |
CloseProtectedSessions | 在发生过登录的地方登出并关闭打开的会话,把令牌释放给其他使用方 |
CK_ULONG 遵循 C 的 unsigned long,在 Windows (LLP64) 上是 4 字节,在 Linux 和 macOS (LP64) 上是 8 字节;每个句柄、计数和属性类型都是 CK_ULONGPkcs11AbiLayout 报告本构建解析出的布局,因此不匹配可被诊断而不是无声崩溃:Win32 报告 ulong=4 attr=12 pss=12 table=2,Win64 报告 ulong=4 attr=16 pss=12 table=2,LP64 目标报告 ulong=8 attr=24 pss=24 table=8CertificateLabel 时,令牌上的第一张证书会被选中,这只适合单证书令牌,不适合任何生产部署