TPDFlibPKCS11Client

Segurança e assinaturas

Descrição

Carrega um módulo PKCS #11 do fornecedor em tempo de execução e expõe certificados e chaves privadas que não estão registados junto dos fornecedores CSP ou KSP do Windows

O cliente enumera os tokens presentes, abre uma sessão persistente, autentica o utilizador, emparelha um certificado X.509 com uma chave de assinatura pelo CKA_ID binário e assina resumos já calculados através de CKM_RSA_PKCS ou CKM_ECDSA

A descoberta explicitamente opcional quanto a certificado pode selecionar uma chave privada ML-DSA por etiqueta ou ID binário, validar o respetivo CKA_PARAMETER_SET e assinar mensagens originais através de CKM_ML_DSA

SignDigest implementa TPDFlibExternalDigestSignEvent e pode ser atribuído diretamente a TPDFlib.OnExternalDigestSign

Unit

PDFlibPKCS11

Construção e ciclo de vida do módulo

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

ModulePath identifica uma DLL PKCS #11 Win32 ou Win64 que exporta C_GetFunctionList

Os clientes que utilizam o mesmo caminho partilham um módulo carregado e um tempo de vida equilibrado de C_Initialize/C_Finalize

O carregador pede primeiro CKF_OS_LOCKING_OK; os módulos que não conseguem fornecer bloqueio interno são protegidos por um bloqueio de chamada partilhado, enquanto os módulos conformes permitem que sessões de clientes separadas corram em simultâneo

UseFunctionList suporta fornecedores estaticamente ligados, embutidos ou geridos pela aplicação; o chamador tem de manter a tabela fornecida viva até UnloadModule ou destruição

Deteção e ligação de tokens

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 enumera os slots presentes com repetição delimitada quando um evento hot-plug muda a contagem, e TokenCount reporta quantos tokens a última atualização encontrouSelecione um token com

SlotID, TokenLabel, TokenSerial ou uma combinação; uma seleção vazia só tem êxito quando existe exatamente um token presente

TPDFlibPKCS11Token reporta a descrição do slot, o fabricante, a etiqueta do token, o modelo, o número de série, os sinalizadores e os limites do comprimento do PIN

Seleção de identidade

CertificateLabelFiltro CKA_LABEL UTF-8 opcional para o objeto de certificado X.509
CertificateIDFiltro CKA_ID binário opcional guardado num AnsiString
PrivateKeyLabelFiltro CKA_LABEL UTF-8 opcional para a chave privada de assinatura
PrivateKeyIDCKA_ID binário opcional da chave privada; quando vazio, o ID do certificado é reutilizado
CertificateOptionalAssume False por predefinição; quando True, é exigida uma etiqueta ou um ID de chave privada e a descoberta aceita apenas chaves ML-DSA sem certificado X.509

A descoberta predefinida exige um certificado e uma chave inequívocos, exige CKA_SIGN=True, valida o DER do certificado e recusa discrepâncias de tipo RSA/EC; a descoberta opcional quanto a certificado exige uma chave ML-DSA inequívoca e valida o conjunto de parâmetros 44, 65 ou 87

O conjunto de parâmetros ML-DSA resolvido é exposto através de MLDSAParameterSet

O certificado resolvido é exposto através de CertificateDER e o ID da chave selecionada através de ResolvedPrivateKeyID

Autenticação

UserPINPIN de utilizador só de escrita codificado em UTF-8, verificado contra os limites do token, e apagado quando substituído ou destruído
UseProtectedAuthenticationPathPassa um PIN nulo quando o token anuncia CKF_PROTECTED_AUTHENTICATION_PATH
ReadWriteSessionAcrescenta CKF_RW_SESSION; a assinatura normalmente precisa apenas da sessão série só de leitura predefinida

Chaves com CKA_ALWAYS_AUTHENTICATE=True recebem um início de sessão específico do contexto após cada C_SignInit bem-sucedido

ClearPIN sobrescreve e liberta o PIN configurado

Assinatura

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;

As assinaturas RSA utilizam CKM_RSA_PKCS bruto com um DigestInfo DER construído localmente, pelo que o token não volta a fazer o hash do resumo do PDF

As assinaturas ECDSA utilizam CKM_ECDSA bruto; a saída P1363 do token é devolvida inalterada para PDF_EXTERNAL_SIGNATURE_ECDSA_P1363 ou convertida em DER estrito para PDF_EXTERNAL_SIGNATURE_ECDSA_DER

SignMLDSA passa a mensagem original para CKM_ML_DSA de parte única com parâmetros de mecanismo vazios, selecionando o contexto vazio predefinido; é independente de SignHash e da chamada de retorno de resumo CMS

Os comprimentos de resumo SHA-1, SHA-256, SHA-384, SHA-512, SHA3-256, SHA3-384 e SHA3-512 são validados antes de qualquer operação com a chave privada

Uma sessão perdida é reaberta e repetida apenas quando C_SignInit falha antes de a assinatura começar, evitando operações de chave privada duplicadas após uma falha ambígua de C_Sign

Exemplo

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;

Estado e comportamento em caso de falha

Connected, KeyType, MLDSAParameterSet, AlwaysAuthenticate, FunctionListMajor e FunctionListMinor expõem o estado do fornecedor ativoOs métodos devolvem

False e definem tanto LastReturnValue como LastError quando o carregamento do módulo, a seleção do token, a autenticação, a descoberta de objetos, os limites de atributos, as verificações de mecanismos ou a assinatura falham

Consulte também

OnExternalDigestSign, SetSignProcessExternalDigestSigner, SetSignProcessDigestAlgorithm, TPDFlibCSCClient

Administração de PIN

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

Ambas as operações abrem a respetiva sessão de leitura/escrita no token configurado e deixam a sessão de assinatura intacta; InitializeUserPIN inicia sessão como oficial de segurança antes de chamar C_InitPIN, e ChangeUserPIN inicia sessão como utilizador antes de chamar C_SetPIN

Os buffers de PIN são apagados após a utilização; um módulo sem o ponto de entrada necessário comunica um erro em vez de falhar silenciosamente