Enterprise edição
Assinatura com HSM — Referência Profunda
Visão geral
Seção intitulada “Visão geral”Esta página é a referência detalhada da superfície de assinatura com HSM do NextPDF Enterprise. Ela cobre três tipos públicos. NextPDF\Enterprise\Security\Signature\Hsm\Pkcs11Signer assina por meio de um token PKCS#11 via extensão ext-pkcs11. NextPDF\Enterprise\Security\Signature\Hsm\OpenSslCliSigner assina por meio do binário openssl em um subprocesso, para chaves apoiadas por provider ou engine que o ext-openssl do PHP não consegue carregar. NextPDF\Enterprise\Security\Signature\Hsm\Provider\HsmSignerProviderAdapter expõe qualquer uma das implementações concretas como um SignerProviderInterface unificado. Em todos os caminhos, a chave privada permanece dentro da fronteira do token; o NextPDF entrega os bytes a serem assinados e recebe a assinatura. O caminho pós-quântico (signPqs) é um preview: está desativado por padrão, não carrega nenhuma alegação de conformidade e não tem caminho de verificação suportado nos validadores de PDF atuais. O NextPDF não detém nenhuma certificação e não concede nenhuma; suporte não é igual a conformidade, e conformidade não é igual a certificação.
Disponibilidade e licenciamento
Seção intitulada “Disponibilidade e licenciamento”Esta capacidade acompanha o NextPDF Enterprise (nextpdf/enterprise) e é ativada com um envelope de licença de nível Enterprise. Uma implantação sem essa habilitação não carrega as classes da capacidade. Compare edições e obtenha uma licença.
Superfície de API pública
Seção intitulada “Superfície de API pública”Todos os três tipos residem em NextPDF\Enterprise\Security\Signature\Hsm; o adaptador fica em seu sub-namespace Provider. Ambos os signers implementam o contrato Core NextPDF\Contracts\HsmSignerInterface.
| Símbolo | Parâmetros | Comportamento padrão | Retorna | Lança ou falha com | Notas |
|---|---|---|---|---|---|
Pkcs11Signer::__construct() | string $libraryPath, int $slotId, string $pin, string $certLabel, ?string $keyLabel = null, array $chainDer = [], bool $enablePostQuantum = false, ?FipsSignatureEnforcer $fipsEnforcer = null | Abre a biblioteca do fornecedor, faz login no slot e carrega o certificado e os metadados de algoritmo da chave a partir do token | — | HsmOperationException quando ext-pkcs11 está ausente ou o acesso ao token falha | Um handle de módulo é cacheado por caminho de biblioteca por processo; PIN e labels são #[SensitiveParameter] |
Pkcs11Signer::sign() | string $data, string $algorithm = 'sha256WithRSAEncryption' | Assina no token; a saída ECDSA bruta é convertida para DER ECDSA-Sig-Value | string bytes brutos da assinatura | HsmOperationException (chave não encontrada, falha do token); InvalidArgumentException (algoritmo não mapeado); exceções do gate FIPS antes de assinar quando um enforcer está conectado | Conjunto de algoritmos fechado; veja Contrato de comportamento |
Pkcs11Signer::signPqs() | string $data, Pkcs11PqsAlgorithm $algorithm, string $context = '', bool $randomized = true | Recusado a menos que $enablePostQuantum tenha sido definido; despacha o mecanismo PQ PKCS#11 provisório | string bytes brutos da assinatura | HsmOperationException (desativado, falha do token, divergência no comprimento da assinatura); InvalidArgumentException (contexto acima de 255 bytes) | Preview; nenhuma alegação de conformidade; os identificadores de mecanismo são provisórios |
Pkcs11Signer::isPostQuantumEnabled() | Nenhum | Reporta a flag de opt-in do construtor | bool | Nenhum | — |
Pkcs11Signer::getCertificateDer() | Nenhum | Retorna o certificado do signer lido do token | string (DER) | Nenhum | Carregado uma vez na construção |
Pkcs11Signer::getCertificateChainDer() | Nenhum | Retorna os intermediários fornecidos ao construtor | array<string> (DER) | Nenhum | Exclui o certificado do signer |
OpenSslCliSigner::__construct() | string $keyUri, string $certPath, string $pin, array $extraCertPaths = [], OpenSslCliBackend $backend = OpenSslCliBackend::Auto, string $opensslBinary = 'openssl', int $timeoutSeconds = 30, ?string $modulePath = null, ?string $configPath = null, bool $legacyPinDelivery = false, ?FipsSignatureEnforcer $fipsEnforcer = null | Verifica proc_open, sonda o binário e a versão, resolve o backend e carrega os certificados | — | HsmOperationException (proc_open desativado, arquivo de módulo/config/certificado ausente, falha do binário, sem backend); InvalidArgumentException (pin-value dentro de $keyUri) | OpenSslCliBackend::Auto prefere o provider OpenSSL 3.x, depois o engine |
OpenSslCliSigner::sign() | string $data, string $algorithm = 'sha256WithRSAEncryption' | Executa openssl dgst em um subprocesso; por padrão, o PIN transita por um arquivo pin-source efêmero 0600 | string bytes brutos da assinatura | HsmOperationException (timeout, PIN rejeitado, chave não encontrada, falha ao carregar o módulo, saída vazia, falha do arquivo de pin); InvalidArgumentException (algoritmo não mapeado); exceções do gate FIPS antes de assinar | O subprocesso é encerrado após $timeoutSeconds; o stderr é redigido antes de chegar às mensagens |
Superfície de acessadores de OpenSslCliSigner | Nenhum | Resultados somente-leitura da construção | string / array<string> / OpenSslCliBackend | Nenhum | getCertificateDer, getCertificateChainDer, getPublicKeyAlgorithm, getCertificatePem, getResolvedBackend, getOpensslVersion |
HsmSignerProviderAdapter::__construct() | HsmSignerInterface $hsm, string $providerId, SignatureAlgorithm $algorithm = SignatureAlgorithm::Pkcs1v15 | Encapsula uma implementação concreta de HSM como um SignerProviderInterface | — | Nenhum | Convenções de id de provider: pkcs11-{module-id}, openssl-cli |
HsmSignerProviderAdapter::providerId() | Nenhum | Retorna o id fornecido ao construtor | non-empty-string | Nenhum | — |
HsmSignerProviderAdapter::supportsAlgorithm() | SignatureAlgorithm $algo | Mapeia o enum para um nome no estilo OpenSSL, depois faz a interseção com o conjunto permitido do backend | bool | Nenhum | Recusa algoritmos apenas de digest; ids openssl-engine não anunciam nada |
HsmSignerProviderAdapter::sign() | string $data, ?string $keyVersion = null | Despacha por meio do signer encapsulado com o algoritmo configurado | non-empty-string | KeyManagementException ($keyVersion não nulo); SignatureFailedException (algoritmo não mapeável, falha do driver, assinatura vazia) | Contrato SPI fail-closed; todo erro de driver aflora tipado |
public function __construct(private readonly string $libraryPath, private readonly int $slotId, #[SensitiveParameter] private readonly string $pin, #[SensitiveParameter] private readonly string $certLabel, #[SensitiveParameter] private readonly ?string $keyLabel = null, array $chainDer = [], private readonly bool $enablePostQuantum = false, ?FipsSignatureEnforcer $fipsEnforcer = null)public function sign(string $data, string $algorithm = 'sha256WithRSAEncryption'): stringpublic function signPqs(string $data, Pkcs11PqsAlgorithm $algorithm, string $context = '', bool $randomized = true): stringpublic function isPostQuantumEnabled(): boolpublic function getCertificateDer(): stringpublic function getCertificateChainDer(): arraypublic function __construct(private string $keyUri, string $certPath, #[SensitiveParameter] private string $pin, array $extraCertPaths = [], private OpenSslCliBackend $backend = OpenSslCliBackend::Auto, private string $opensslBinary = 'openssl', private int $timeoutSeconds = 30, private ?string $modulePath = null, private ?string $configPath = null, private bool $legacyPinDelivery = false, private ?FipsSignatureEnforcer $fipsEnforcer = null)public function sign(string $data, string $algorithm = 'sha256WithRSAEncryption'): stringpublic function getCertificateDer(): stringpublic function getCertificateChainDer(): arraypublic function getPublicKeyAlgorithm(): stringpublic function getCertificatePem(): stringpublic function getResolvedBackend(): OpenSslCliBackendpublic function getOpensslVersion(): stringpublic function __construct(private HsmSignerInterface $hsm, private string $providerId, private SignatureAlgorithm $algorithm = SignatureAlgorithm::Pkcs1v15)public function providerId(): stringpublic function supportsAlgorithm(SignatureAlgorithm $algo): boolpublic function sign(string $data, ?string $keyVersion = null): stringContrato de comportamento
Seção intitulada “Contrato de comportamento”- Custódia da chave. A chave privada nunca sai da fronteira do token. O
Pkcs11Signerdelega a operação ao token; oOpenSslCliSignerpassa uma referência de chave — uma URI PKCS#11 — ao subprocessoopenssl. Nenhum dos signers consegue exportar a chave. - Sessão e login. O
Pkcs11Signercacheia um handle de módulo PKCS#11 por caminho de biblioteca por processo, porque a interface do token deve ser inicializada exatamente uma vez por processo. Cada operação abre uma sessão e faz login com o PIN; o login autentica o usuário antes de qualquer uso da chave privada (PKCS#11 v3.1 §5.6.8). Quando o slot reporta um login existente, o signer faz logout e login novamente, de modo que tokens que exigem um PIN novo por operação recebam um. - Conjunto de algoritmos (fechado). Ambos os signers aceitam exatamente:
sha256WithRSAEncryption,sha384WithRSAEncryption,sha512WithRSAEncryption;RSASSA-PSS,RSASSA-PSS-SHA256,RSASSA-PSS-SHA384,RSASSA-PSS-SHA512;ecdsa-with-SHA256,ecdsa-with-SHA384,ecdsa-with-SHA512. OPkcs11Signeraceita adicionalmenteecdsa-raw. Qualquer outro identificador levantaInvalidArgumentException— nenhum algoritmo substituto é jamais assinado. - Vínculo de salt do PSS. Para toda variante PSS, o comprimento do salt é igual ao comprimento do digest — 32, 48 ou 64 bytes — e os parâmetros de hash e MGF correspondem ao digest escolhido. Isto segue a estrutura de parâmetros do mecanismo PSS, na qual o comprimento do salt é tipicamente o comprimento do hash da mensagem (PKCS#11 v3.1 §6.1.9). Ambos os signers aplicam o mesmo pareamento, portanto uma configuração válida em um backend é válida no outro.
- Conversão ECDSA. Um token retorna uma assinatura ECDSA como a concatenação bruta, com zero-padding, de r e s (PKCS#11 v3.1 §6.3.1). O
Pkcs11Signer::sign()converte essa saída para a formaECDSA-Sig-Valuecodificada em DER que os validadores de PDF e o OpenSSL esperam. O chamador nunca manipula a forma bruta. - Entrega do PIN (caminho CLI). No padrão seguro, o PIN é gravado em um arquivo efêmero criado exclusivamente com permissões apenas do dono, referenciado por meio do atributo
pin-sourceda URI PKCS#11, e removido após o subprocesso encerrar. Nesse modo, o PIN não é colocado na linha de comando e não é exportado para o ambiente do subprocesso. Com$legacyPinDelivery = true, o PIN é embutido comopin-valuena URI, o que é observável na linha de comando do processo; esse modo é opt-in apenas. - Disciplina do subprocesso. O
OpenSslCliSignergera o binário com um array de argumentos — sem interpolação de shell —, impõe$timeoutSeconds, encerra o subprocesso na expiração e classifica o stderr em erros tipados. Segredos são redigidos do stderr antes de serem citados em uma mensagem de exceção. - Semântica do adaptador. Um token HSM não tem conceito de versão de chave gerenciada; a chave no token é a versão. O
HsmSignerProviderAdapter::sign()portanto rejeita qualquer$keyVersionnão nulo comKeyManagementExceptionem vez de ignorá-lo. OsupportsAlgorithm()faz a interseção do mapeamento do enum com o conjunto aceito pelo backend encapsulado, de modo que o adaptador nunca anuncia um mecanismo que o backend rejeitaria no momento da assinatura. Uma assinatura vazia vinda do driver levantaSignatureFailedException. - Preview pós-quântico. O
signPqs()é protegido pela flag de construtor$enablePostQuantume se recusa a executar de outra forma. A string de contexto é limitada a 255 bytes, correspondendo ao limite de contexto do ML-DSA (FIPS 204). A assinatura retornada deve corresponder ao comprimento exato em bytes do conjunto de parâmetrosPkcs11PqsAlgorithmselecionado, ou a chamada falha. Os identificadores de mecanismo seguem uma extensão PQ PKCS#11 provisória e não são finais. Os perfis PAdES não reconhecem suítes pós-quânticas, a maioria dos validadores de PDF rejeita tais assinaturas e o NextPDF não fornece nenhum caminho de verificação para elas. Nenhuma conformidade é alegada.
Casos extremos e modos de falha
Seção intitulada “Casos extremos e modos de falha”- Construir
Pkcs11Signersemext-pkcs11levantaHsmOperationExceptionimediatamente; a extensão não é empacotada com as distribuições PHP padrão. - Um label de certificado ou de chave privada que não corresponde a nenhum objeto no token levanta
HsmOperationExceptionnomeando a classe do objeto ausente. O label da chave pode legitimamente diferir do label do certificado em alguns tokens. - Logins malsucedidos repetidos podem travar o PIN no token; o token impõe essa política, não o NextPDF. Tokens cujas chaves exigem autenticação a cada uso recebem um login novo por meio do caminho de logout e nova tentativa (PKCS#11 v3.1, semântica always-authenticate).
- O
OpenSslCliSignerrecusa uma$keyUrique já contémpin-valuena construção, fail-closed, porque essa entrega contornaria o caminho seguro do PIN. - No Windows, o modo seguro de arquivo de pin falha fechado com
HsmOperationException: os bits de permissão de arquivo não conseguem restringir concessões de leitura de ACL lá, então o signer se recusa a deixar um PIN em texto claro na ACL do diretório temporário. A entrega legada de PIN é a alternativa documentada e opt-in para hosts Windows confiáveis. - A autodetecção de backend requer OpenSSL 3.x para o caminho do provider; o LibreSSL nunca resolve para o provider. Quando nem uma sondagem de provider nem de engine tem sucesso, a construção falha com
HsmOperationExceptionem vez de adiar a falha para o momento da assinatura. - Um subprocesso que excede
$timeoutSecondsé encerrado e reportado como timeout; um subprocesso que sai limpo com saída vazia é reportado como falha de assinatura vazia. Nenhuma das condições pode produzir um documento parcialmente assinado. - Uma assinatura pós-quântica cujo comprimento em bytes não corresponde ao conjunto de parâmetros selecionado é rejeitada antes que possa chegar à codificação CMS.
- O
HsmSignerProviderAdaptercom o id de provider aposentadoopenssl-enginenão anuncia nenhum algoritmo, então uma configuração obsoleta falha na seleção do provider em vez de no momento da assinatura.
Comportamento em modo FIPS
Seção intitulada “Comportamento em modo FIPS”Ambos os signers aceitam um FipsSignatureEnforcer opcional. Quando um está conectado, o modo FIPS está ativo para aquele signer: sign() rejeita um algoritmo de assinatura não permitido ou uma chave abaixo do piso antes que qualquer assinatura no token ou no subprocesso ocorra. Os pisos seguem a tabela de geração de assinaturas — módulos RSA abaixo de 2048 bits e ordens ECDSA abaixo de 224 bits são proibidos (NIST SP 800-131A Rev.2 §3 Table 2). Sem um enforcer, o comportamento é inalterado. O gate cobre apenas o caminho clássico sign(); o signPqs() é governado por sua própria flag de preview. Estas são alegações de capacidade sobre o código NextPDF: a validação FIPS 140-3 se vincula a um módulo criptográfico por meio do CMVP, que nesta implantação é o HSM ou provider do operador — o NextPDF não é um módulo validado, não detém nenhuma certificação e não concede nenhuma.
Conformidade
Seção intitulada “Conformidade”| Alegação | Padrão | Cláusula |
|---|---|---|
| O login autentica o usuário no token antes das operações com a chave privada; um PIN incorreto nega o acesso. | PKCS#11 v3.1 | §5.6.8 |
| Chaves always-authenticate precisam de um login novo por uso; nova autenticação malsucedida repetida pode travar o PIN. | PKCS#11 v3.1 | CKA_ALWAYS_AUTHENTICATE re-authentication |
| Uma assinatura ECDSA de token é a concatenação bruta r‖s; o signer a converte para DER visando interoperabilidade com PDF. | PKCS#11 v3.1 | §6.3.1 |
| Os parâmetros PSS vinculam hash, MGF e comprimento do salt; os signers definem o salt igual ao comprimento do digest. | PKCS#11 v3.1 | §6.1.9 |
| O gate FIPS nega a geração de assinatura com RSA abaixo de 2048 bits ou ordem ECDSA abaixo de 224 bits. | NIST SP 800-131A Rev.2 | §3 Table 2 |
| A string de contexto pós-quântica é limitada a 255 bytes. | FIPS 204 | HashML-DSA context handling |
| A validação FIPS 140-3 se vincula a módulos criptográficos por meio do CMVP. | FIPS 140-3 | CMVP program scope |
Todas as cláusulas são parafraseadas; nenhum texto normativo é reproduzido. O NextPDF não faz nenhuma alegação de certificação. Os signers alinham seu comportamento com as cláusulas citadas como uma capacidade. Se uma assinatura produzida verifica é a decisão do verificador contra suas âncoras de confiança; a segurança da chave depende do token, do HSM e do operador — não do NextPDF isoladamente.
Notas de desenvolvimento
Seção intitulada “Notas de desenvolvimento”-
O mecanismo de entrega do PIN segue a convenção
pin-sourceda URI PKCS#11 (RFC 7512); esse RFC está fora do corpus citado, então o comportamento acima é fundamentado no código-fonte do produto, não em uma citação de spec. -
Confirme que o runtime carrega
ext-pkcs11antes de construir oPkcs11Signer; a construção falha rápido quando a extensão está ausente. O signer CLI precisa deproc_openhabilitado e de um binárioopensslcom um provider ou engine PKCS#11 instalado. -
O PIN, o label do certificado e o label da chave são
#[SensitiveParameter], então são excluídos dos stack traces. Forneça o PIN a partir de um gerenciador de segredos; nunca o grave em código-fonte, em configuração commitada ao controle de versão, ou em logs. -
A construção é o passo caro em ambos os signers: o caminho PKCS#11 faz login e lê o certificado, e o caminho CLI sonda o binário e o backend. Construa uma vez e reutilize a instância; o cache de módulo por biblioteca torna a construção repetida contra a mesma biblioteca segura.
-
Encapsule um signer em
HsmSignerProviderAdapterquando o chamador trabalha por meio deSignerProviderInterface. Passe o id de provider canônico para a classe encapsulada —pkcs11-{module-id}ouopenssl-cli— de modo que as verificações de capacidade usem o conjunto permitido do backend correto. -
Antes de habilitar o preview pós-quântico, verifique os identificadores de mecanismo do firmware do token contra os valores provisórios que o NextPDF registra; uma divergência falha no momento da assinatura. Não habilite o preview para saída PAdES de produção.
-
getResolvedBackend()egetOpensslVersion()existem para registro de evidências; persista-os junto com as evidências de assinatura quando seu programa de conformidade exigir reprodutibilidade.
Veja também
Seção intitulada “Veja também”- Assinatura com módulo de segurança de hardware (PKCS#11) — a página da capacidade com passos de configuração, ajustes e verificação.
- Segurança — Referência Detalhada — a superfície de segurança combinada do Enterprise.
- Assinatura — Referência Detalhada — o produtor de longo prazo PAdES B-LT / B-LTA.
- FIPS 140 — Referência Profunda — a política de cripto, a bateria de autotestes e o gate
FipsSignatureEnforcer. - Preview PQC — Referência Detalhada — a superfície do preview pós-quântico e seus limites.
- Segurança / Assinatura (Core) — o signer CMS do Core e os contratos de assinatura.
Fronteira de publicação
Seção intitulada “Fronteira de publicação”Esta página documenta apenas o comportamento observável externamente e a superfície de API pública suportada. Caminhos de namespace internos, classes auxiliares, tabelas de mecanismos, nomes de arquivos de runbook e prefixos de tickets estão fora do escopo.