TPDFlibPKCS11Client

安全和签名

描述

在运行时加载厂商的 PKCS #11 模块,暴露未注册到 Windows CSP 或 KSP 提供程序的证书和私钥

客户端枚举在位令牌、打开持久会话、认证用户、以二进制 CKA_ID 把 X.509 证书与签名密钥配对,并通过 CKM_RSA_PKCS 或 CKM_ECDSA 签署已计算的摘要

显式的证书可选发现可按标签或二进制 ID 选择 ML-DSA 私钥,验证其 CKA_PARAMETER_SET,并通过 CKM_ML_DSA 签署原始消息

SignDigest 实现 TPDFlibExternalDigestSignEvent,可直接赋给 TPDFlib.OnExternalDigestSign

单元

PDFlibPKCS11

构造与模块生命周期

Constructor TPDFlibPKCS11Client.Create;
Destructor TPDFlibPKCS11Client.Destroy;
Function TPDFlibPKCS11Client.LoadModule: Boolean;
Procedure TPDFlibPKCS11Client.UnloadModule;
Function TPDFlibPKCS11Client.UseFunctionList(FunctionList: PPLPKCS11FunctionList; AlreadyInitialized: Boolean): Boolean;

ModulePath 标识导出 C_GetFunctionList 的 Win32 或 Win64 PKCS #11 DLL

使用相同路径的客户端共享一个已加载模块和一个配平的 C_Initialize/C_Finalize 生命周期

加载器先请求 CKF_OS_LOCKING_OK;无法提供内部锁定的模块由共享调用锁保护,而合规模块允许不同客户端会话并发运行

UseFunctionList 支持静态链接、内嵌或应用程序管理的提供程序;调用方必须使所提供的函数表保持有效,直到 UnloadModule 或销毁

令牌发现和连接

Function TPDFlibPKCS11Client.RefreshTokens: Boolean;
Function TPDFlibPKCS11Client.GetToken(Index: Integer; Out Token: TPDFlibPKCS11Token): Boolean;
Function TPDFlibPKCS11Client.Connect: Boolean;
Procedure TPDFlibPKCS11Client.Disconnect;
Function TPDFlibPKCS11Client.GetSelectedToken(Out Token: TPDFlibPKCS11Token): Boolean;
Property TPDFlibPKCS11Client.TokenCount: Integer;

RefreshTokens 在热插拔事件改变数量时以有界重试枚举当前存在的槽位,TokenCount 报告上次刷新发现的令牌数量使用

SlotID、TokenLabel、TokenSerial 或其组合选择令牌;空选择仅在恰好存在一个令牌时成功

TPDFlibPKCS11Token 报告槽位描述、制造商、令牌标签、型号、序列号、标志和 PIN 长度边界

标识选择

CertificateLabelX.509 证书对象的可选 UTF-8 CKA_LABEL 过滤器
CertificateID存储在 AnsiString 中的可选二进制 CKA_ID 过滤器
PrivateKeyLabel私钥签名密钥的可选 UTF-8 CKA_LABEL 过滤器
PrivateKeyID可选的二进制私钥 CKA_ID;为空时复用证书 ID
CertificateOptional默认 False;为 True 时要求私钥标签或 ID,且发现只接受没有 X.509 证书的 ML-DSA 密钥

默认发现要求一对无歧义的证书与密钥、要求 CKA_SIGN=True、验证证书 DER,并拒绝 RSA/EC 类型不匹配;证书可选发现要求无歧义的 ML-DSA 密钥,并验证参数集 44、65 或 87

解析出的 ML-DSA 参数集通过 MLDSAParameterSet 暴露

解析出的证书通过 CertificateDER 暴露,所选密钥 ID 通过 ResolvedPrivateKeyID 暴露

身份验证

UserPIN只写的用户 PIN,以 UTF-8 编码,会对照令牌边界检查,并在替换或销毁时被擦除
UseProtectedAuthenticationPath令牌声明 CKF_PROTECTED_AUTHENTICATION_PATH 时传入空 PIN
ReadWriteSession添加 CKF_RW_SESSION;签名通常只需要默认的只读串行会话

具有 CKA_ALWAYS_AUTHENTICATE=True 的密钥在每次成功的 C_SignInit 之后都会进行上下文特定的登录

ClearPIN 覆盖并释放已配置的 PIN

签名

Function TPDFlibPKCS11Client.SupportsMechanism(MechanismType: Cardinal): Boolean;
Function TPDFlibPKCS11Client.SupportsAlgorithm(DigestAlgorithm, SignatureAlgorithm: Integer): Boolean;
Function TPDFlibPKCS11Client.SignMLDSA(Const Message: AnsiString; Out Signature: AnsiString): Boolean;
Function TPDFlibPKCS11Client.SignHash(Const Digest: AnsiString; DigestAlgorithm, SignatureAlgorithm: Integer; Out Signature: AnsiString): Boolean;
Function TPDFlibPKCS11Client.SignDigest(Sender: TObject; SignProcessID: Integer; Const Digest: AnsiString; DigestAlgorithm, SignatureAlgorithm: Integer; Var Signature: AnsiString): Boolean;

RSA 签名使用带本地构造 DER DigestInfo 的原始 CKM_RSA_PKCS,因此令牌不会对 PDF 摘要进行第二次哈希

ECDSA 签名使用原始 CKM_ECDSA;令牌的 P1363 输出在 PDF_EXTERNAL_SIGNATURE_ECDSA_P1363 下原样返回,在 PDF_EXTERNAL_SIGNATURE_ECDSA_DER 下转换为严格 DER

SignMLDSA 把原始消息传给带空机制参数的单部分 CKM_ML_DSA,选择默认的空上下文;它与 SignHash 和 CMS 摘要回调相互独立

SHA-1、SHA-256、SHA-384、SHA-512、SHA3-256、SHA3-384 和 SHA3-512 摘要长度在任何私钥操作之前验证

只有在签名开始前 C_SignInit 失败时才重新打开并重试丢失的会话,以避免在结果不明的 C_Sign 失败后重复私钥操作

示例

var
  Token: TPDFlibPKCS11Client;
  ProcessID: Integer;
begin
  Token:= TPDFlibPKCS11Client.Create;
  try
    Token.ModulePath:= 'C:\Program Files\Vendor\pkcs11.dll';
    Token.TokenLabel:= 'Signing token';
    Token.CertificateLabel:= 'Document signer';
    Token.UserPIN:= UserPIN;
    if not Token.Connect then
      raise Exception.Create(String(Token.LastError));

    PDF.OnExternalDigestSign:= Token.SignDigest;
    ProcessID:= PDF.NewSignProcessFromFile(InputFile, '');
    PDF.SetSignProcessField(ProcessID, 'Approval');
    PDF.SetSignProcessDigestAlgorithm(ProcessID, 2);
    PDF.SetSignProcessExternalDigestSigner(ProcessID,
      Token.CertificateDER, PDF_EXTERNAL_SIGNATURE_RSA_PKCS1, 512);
    PDF.EndSignProcessToFile(ProcessID, OutputFile);
  finally
    Token.Free;
  end;
end;

状态与失败行为

Connected、KeyType、MLDSAParameterSet、AlwaysAuthenticate、FunctionListMajor 和 FunctionListMinor 公开活动的提供程序状态当模块加载、令牌选择、身份验证、对象发现、属性边界、机制检查或签名失败时,方法会返回

False 并同时设置 LastReturnValue 和 LastError

另请参阅

OnExternalDigestSign, SetSignProcessExternalDigestSigner, SetSignProcessDigestAlgorithm, TPDFlibCSCClient

PIN 管理

Function TPDFlibPKCS11Client.InitializeUserPIN(Const SecurityOfficerPIN, NewUserPIN: WideString): Boolean;
Function TPDFlibPKCS11Client.ChangeUserPIN(Const OldPIN, NewPIN: WideString): Boolean;

两个操作都在配置的令牌上打开各自的读写会话而不触动签名会话;InitializeUserPIN 在调用 C_InitPIN 前以安全官身份登录,ChangeUserPIN 在调用 C_SetPIN 前以用户身份登录

PIN 缓冲区在使用后清零;缺少所需入口点的模块会报错而不是静默失败