Pular para o conteúdo
getnextpdf.com

Enterprise edição

Assinatura por hardware security module (PKCS#11)

O NextPDF Enterprise assina um PDF com uma chave mantida dentro de um hardware security module (HSM). Você aponta o signatário para um token PKCS#11 — um smart card, um token Universal Serial Bus (USB) ou um HSM em rede — e a operação de assinatura é executada no dispositivo. A chave privada nunca sai do limite do token. Esta página é de nível de comportamento: ela declara o que o signatário faz, o que você fornece e onde a custódia de chaves deixa de ser responsabilidade do NextPDF.

O signatário HSM é resolvido por meio do contrato de signatário do Core, de modo que a sua aplicação depende do contrato, não do tipo concreto do Enterprise. Ele expande o mesmo caminho de assinatura Cryptographic Message Syntax (CMS) que o Core usa, exceto que a operação criptográfica é delegada ao token.

Os pré-requisitos são declarados no front matter e repetidos em Pré-requisitos, para que você não seja surpreendido no meio da tarefa.

Este recurso é entregue no NextPDF Enterprise (nextpdf/enterprise) e é ativado com um envelope de licença de nível Enterprise. Uma implantação sem esse direito de uso não carrega as classes do recurso. Compare edições e obtenha uma licença.

O NextPDF Core entrega um signatário CMS por software que mantém a chave em processo ou aceita uma por meio do contrato de estratégia de assinatura do Core; o NextPDF Pro acrescenta estratégias de assinatura remotas e de key-management-service (KMS) na nuvem. A custódia de chaves em hardware por meio de PKCS#11 é um recurso do Enterprise, não fornecido pelo Core nem pelo Pro.

Um token PKCS#11 expõe objetos criptográficos — certificados e chaves privadas — por trás de uma biblioteca compartilhada do fornecedor. O signatário do Enterprise adapta essa biblioteca:

  1. Ele abre a biblioteca compartilhada do token uma vez por processo e armazena em cache o handle do módulo, porque o PKCS#11 exige que o módulo seja inicializado exatamente uma vez por processo.
  2. Ele abre uma sessão no slot configurado e faz login com o PIN fornecido. O login autentica o usuário antes de qualquer operação de chave privada, conforme o PKCS#11 v3.1 §5.6.8.
  3. Ele localiza o certificado de assinatura no token pelo label, lê o certificado na forma Distinguished Encoding Rules (DER) e detecta o algoritmo de chave pública.
  4. No momento da assinatura, ele localiza a chave privada pelo label — que pode diferir do label do certificado em alguns tokens — e pede ao token para computar a assinatura. Os dados a assinar são passados; a chave permanece no dispositivo.

O signatário suporta RSA com padding PKCS#1 v1.5 (SHA-256, SHA-384, SHA-512), RSA com padding Probabilistic Signature Scheme (PSS) em que o comprimento do salt é igual ao comprimento do resumo, e Elliptic Curve Digital Signature Algorithm (ECDSA) com SHA-256, SHA-384 e SHA-512. A curva e o resumo do ECDSA são emparelhados convencionalmente — P-256 com SHA-256, P-384 com SHA-384, P-521 com SHA-512 — seguindo o emparelhamento recomendado na RFC 5480. Um token retorna uma assinatura ECDSA como uma concatenação bruta dos dois inteiros; o signatário a converte para a forma codificada em DER que o PDF e o OpenSSL esperam.

Para a geração de assinaturas, uma chave RSA de pelo menos 2048 bits e uma ordem de curva ECDSA de pelo menos 224 bits são os mínimos aceitáveis conforme a NIST SP 800-131A Rev.2 §3. Provisione a chave do seu token nesses tamanhos ou acima deles.

Existe um caminho alternativo via engine do OpenSSL para tokens apoiados em engine. No OpenSSL 3.x, a extensão OpenSSL do PHP não expõe a application programming interface (API) de engine, então a classe de engine está descontinuada; a rota apoiada em engine suportada executa o binário de linha de comando do OpenSSL. Prefira o caminho PKCS#11 direto onde o seu token tiver uma biblioteca PKCS#11.

A decisão determinante é que a chave privada nunca sai do token. Por isso, o signatário delega a operação criptográfica ao dispositivo e move apenas os dados a assinar através da fronteira do PKCS#11. Ele nunca lê nem reconstrói material de chave na memória do PHP. Ele é resolvido por meio do contrato HsmSignerInterface do Core, em vez de um tipo concreto do Enterprise, de modo que o código de assinatura é idêntico quer a chave viva em software, em um KMS na nuvem ou em um token de hardware. Ele armazena em cache o handle do módulo uma vez por processo porque o PKCS#11 inicializa cada módulo exatamente uma vez por processo, e então converte a saída bruta ECDSA do token para DER, de modo que os validadores vejam a codificação que esperam. A custódia, não a conveniência, molda o formato: o limite de confiança permanece na borda do dispositivo.

Contexto de design: Assinatura apoiada em HSM.

Antes de assinar com um HSM, confirme cada item:

  1. Instale o NextPDF Core e o pacote Enterprise: composer require nextpdf/core:^3 e composer require nextpdf/enterprise.
  2. Mantenha uma licença NextPDF Enterprise ativa; resolva o pacote com as suas credenciais de licença no Private Packagist.
  3. Instale a biblioteca compartilhada PKCS#11 do fornecedor do token no host (por exemplo, um .so no Linux ou uma .dll no Windows) e anote seu caminho absoluto, o número do slot e os labels dos objetos.
  4. Carregue a extensão PHP ext-pkcs11. Ela não vem incluída no PHP padrão e deve ser instalada separadamente. O construtor do signatário lança um erro de operação tipado quando a extensão está ausente.

Forneça estas entradas ao signatário:

  • Caminho da biblioteca — o caminho absoluto para a biblioteca compartilhada PKCS#11 do fornecedor.
  • Identificador do slot — o número do slot do token, tipicamente 0.
  • PIN — o PIN do token. Trate-o como um segredo: forneça-o a partir do seu gerenciador de segredos, nunca do código-fonte ou de logs. O signatário marca o parâmetro do PIN como sensível, de modo que ele é excluído de stack traces e da serialização.
  • Label do certificado — o label do objeto de certificado no token.
  • Label da chave — o label do objeto de chave privada, quando ele difere do label do certificado.
  • Cadeia — certificados intermediários opcionais na forma DER, quando o token não os mantém.

Verifique a disponibilidade do token antes de construir o signatário. A construção lê o certificado do token, então um slot ou label mal configurado falha rapidamente com um erro tipado, em vez de no momento da assinatura.

  1. Confirme que o runtime suporta PKCS#11 verificando a disponibilidade da extensão. Não construa o signatário quando a extensão estiver ausente.
  2. Leia o PIN do seu gerenciador de segredos para uma variável que nunca é registrada em log.
  3. Construa o signatário HSM com o caminho da biblioteca, o slot, o PIN e os labels. A construção faz login e lê o certificado.
  4. Passe o signatário ao orquestrador de assinatura do Core por meio de HsmSignerInterface. O orquestrador computa o byte range, constrói os atributos assinados CMS, entrega os dados ao token e monta o PDF assinado.
  5. Capture a falha mais específica, registre em log uma mensagem estrutural sem o PIN e relance a exceção.
examples/contracts/hsm-signer-availability.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\HsmSignerInterface;
/**
* Build a hardware-token signer only when the runtime supports it.
*
* The concrete PKCS#11 signer is resolved through the Core contract so the
* caller depends on the interface, not the Enterprise implementation type.
* The PIN arrives from a secret resolver; it is never written to source.
*
* @param callable(): bool $pkcs11Available Reports ext-pkcs11 availability.
* @param callable(): HsmSignerInterface $signerFactory Builds the configured token signer.
*
* @throws \RuntimeException When the PKCS#11 extension is not loaded.
*
* @return HsmSignerInterface The token signer, ready for the Core orchestrator.
*/
function resolveHsmSigner(callable $pkcs11Available, callable $signerFactory): HsmSignerInterface
{
if ($pkcs11Available() !== true) {
throw new \RuntimeException(
'PKCS#11 signing requires the ext-pkcs11 extension; install it before signing.',
);
}
return $signerFactory();
}

A fiação de produção — a lista exata de argumentos do construtor e os tipos de exceção tipados — está documentada na referência aprofundada de HSM.

examples/contracts/hsm-sign-guarded.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\HsmSignerInterface;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
final readonly class HsmSigningService
{
public function __construct(
private HsmSignerInterface $signer,
private LoggerInterface $logger,
) {}
/**
* Sign data on the token through the Core HSM contract.
*
* The byte range is computed by the engine, never accepted from the
* caller. The token performs the signing operation; the private key
* does not leave the device.
*
* @param string $data The bytes the orchestrator hands to the token.
* @param string $algorithm The OpenSSL-style signing algorithm identifier.
*
* @throws NextPdfException When the token operation fails.
*
* @return string The raw signature bytes returned by the token.
*/
public function sign(string $data, string $algorithm): string
{
try {
return $this->signer->sign($data, $algorithm);
} catch (NextPdfException $e) {
// Structural message only — never the PIN or key material.
$this->logger->error('HSM signing failed', ['reason' => $e->getMessage()]);
throw $e;
}
}
}

Confirme o resultado da forma como um verificador faria:

  1. Leia de volta o certificado do signatário e a cadeia na forma DER a partir do signatário e confirme que correspondem ao certificado provisionado no token.
  2. Abra o PDF assinado em um validador configurado com as suas âncoras de confiança e confirme que a assinatura é relatada como criptograficamente íntegra. Uma assinatura produzida não é uma assinatura verificada; a decisão de confiança pertence ao verificador e às suas âncoras de confiança, não ao produtor.
  3. Para uma assinatura ECDSA, confirme que a assinatura incorporada está codificada em DER — o signatário converte a saída bruta do token para você, então um validador que rejeita a forma concatenada bruta ainda deve aceitar a assinatura incorporada.
  4. Confirme que nenhum PIN, label de token ou material de chave aparece nos logs da sua aplicação.
  • A chave permanece no token. Os dados a assinar são entregues ao token; a operação de assinatura é executada dentro do limite do token. A chave privada nunca é carregada na memória do PHP.
  • O PIN é um segredo. Ele é um parâmetro de construtor sensível, excluído dos logs e da serialização. Forneça-o a partir de um gerenciador de segredos. Reautenticações falhas repetidas podem bloquear o PIN no token; o token, não o NextPDF, impõe essa política.
  • Fail-closed. Um erro de token ou de HSM lança uma exceção tipada. O signatário não produz um resultado não assinado ou parcialmente assinado e nunca substitui por um algoritmo mais fraco.
  • Força do algoritmo. Provisione chaves RSA de pelo menos 2048 bits e curvas ECDSA de pelo menos 224 bits de ordem, os mínimos aceitáveis para a geração de assinaturas conforme a NIST SP 800-131A Rev.2 §3.
  • A assinatura pós-quântica é experimental e está desativada por padrão. Existe um caminho pós-quântico por trás de um sinalizador de opt-in explícito. Os perfis de arquivamento de longo prazo PDF Advanced Electronic Signatures (PAdES) padrão ainda não reconhecem suítes pós-quânticas, e a maioria dos visualizadores as rejeita na validação. Não o ative para assinaturas PAdES de produção.

Esta página trata de assinatura criptográfica e de integração com hardware security module. Toda fonte normativa é parafraseada; nenhum texto normativo é reproduzido. ### Limite de custódia de chaves

O NextPDF Enterprise integra-se a um token PKCS#11 ou HSM. Ele não armazena, não gera nem garante a segurança da chave de assinatura. A segurança da chave depende do token ou HSM, da implantação e do operador — não apenas do NextPDF Enterprise. Você é responsável pelo provisionamento do token, pelo manuseio do PIN, pela configuração do slot e pela proteção de rede de um HSM em rede.

  • Extensão ausente. Construir o signatário PKCS#11 lança uma exceção de operação tipada quando ext-pkcs11 não está carregada. Verifique a disponibilidade primeiro.
  • Certificado ou chave não encontrado pelo label. A construção ou a assinatura lança uma exceção tipada que nomeia o objeto ausente. Confirme o label e o slot.
  • Já logado. Quando várias instâncias de signatário compartilham um módulo em cache para o mesmo slot, o signatário faz logout e login novamente para fornecer uma verificação de PIN nova — exigida por tokens de personal-identity-verification com uma política de “PIN a cada vez”.
  • Algoritmo não suportado. Solicitar um algoritmo que o signatário não mapeia lança um erro de argumento, em vez de assinar com um substituto.
  • HSM em rede inacessível. Um erro de rede ou de dispositivo lança uma exceção tipada; o signatário nunca produz silenciosamente um documento não assinado.

Esta página documenta apenas o comportamento observável externamente e a superfície pública de API suportada. Caminhos internos de namespace, classes auxiliares, tabelas de mecanismos, nomes de arquivos de runbook e prefixos de tickets estão fora de escopo.