Enterprise édition
Signature HSM — Référence détaillée
Cette page est la référence détaillée de la surface de signature HSM de NextPDF Enterprise. Elle couvre trois types publics. NextPDF\Enterprise\Security\Signature\Hsm\Pkcs11Signer signe via un jeton PKCS#11 à travers l’extension ext-pkcs11. NextPDF\Enterprise\Security\Signature\Hsm\OpenSslCliSigner signe via le binaire openssl dans un sous-processus, pour des clés adossées à un fournisseur ou à un moteur que le PHP ext-openssl ne peut pas charger. NextPDF\Enterprise\Security\Signature\Hsm\Provider\HsmSignerProviderAdapter expose l’un ou l’autre concret comme un SignerProviderInterface unifié. Dans chaque voie, la clé privée reste à l’intérieur de la frontière du jeton ; NextPDF remet les octets à signer et reçoit la signature. La voie post-quantique (signPqs) est un aperçu : elle est désactivée par défaut, ne porte aucune revendication de conformité et n’a aucune voie de vérification prise en charge dans les validateurs PDF actuels. NextPDF ne détient aucune certification et n’en accorde aucune ; la prise en charge n’équivaut pas à la conformité, et la conformité n’équivaut pas à la certification.
Disponibilité et licence
Section intitulée « Disponibilité et licence »Cette capacité est livrée dans NextPDF Enterprise (nextpdf/enterprise) et s’active avec une enveloppe de licence de niveau Enterprise. Un déploiement sans ce droit ne charge pas les classes de la capacité. Comparer les éditions et obtenir une licence.
Surface d’API publique
Section intitulée « Surface d’API publique »Les trois types vivent dans NextPDF\Enterprise\Security\Signature\Hsm ; l’adaptateur se trouve dans son sous-espace de noms Provider. Les deux signataires implémentent le contrat Core NextPDF\Contracts\HsmSignerInterface.
| Symbole | Paramètres | Comportement par défaut | Renvoie | Lève ou échoue avec | Notes |
|---|---|---|---|---|---|
Pkcs11Signer::__construct() | string $libraryPath, int $slotId, string $pin, string $certLabel, ?string $keyLabel = null, array $chainDer = [], bool $enablePostQuantum = false, ?FipsSignatureEnforcer $fipsEnforcer = null | Ouvre la bibliothèque fournisseur, se connecte au slot et charge le certificat et les métadonnées d’algorithme de clé depuis le jeton | — | HsmOperationException lorsque ext-pkcs11 est absent ou que l’accès au jeton échoue | Un handle de module est mis en cache par chemin de bibliothèque par processus ; le PIN et les labels sont #[SensitiveParameter] |
Pkcs11Signer::sign() | string $data, string $algorithm = 'sha256WithRSAEncryption' | Signe sur le jeton ; la sortie ECDSA brute est convertie en DER ECDSA-Sig-Value | string octets de signature bruts | HsmOperationException (clé introuvable, défaillance du jeton) ; InvalidArgumentException (algorithme non mappé) ; exceptions du garde-fou FIPS avant la signature lorsqu’un enforcer est câblé | Ensemble d’algorithmes fermé ; voir Contrat de comportement |
Pkcs11Signer::signPqs() | string $data, Pkcs11PqsAlgorithm $algorithm, string $context = '', bool $randomized = true | Refusé sauf si $enablePostQuantum a été défini ; dispatche le mécanisme PQ PKCS#11 provisoire | string octets de signature bruts | HsmOperationException (désactivé, défaillance du jeton, incohérence de longueur de signature) ; InvalidArgumentException (contexte au-delà de 255 octets) | Aperçu ; aucune revendication de conformité ; les identifiants de mécanisme sont provisoires |
Pkcs11Signer::isPostQuantumEnabled() | Aucun | Signale le drapeau d’activation du constructeur | bool | Aucun | — |
Pkcs11Signer::getCertificateDer() | Aucun | Renvoie le certificat du signataire lu depuis le jeton | string (DER) | Aucun | Chargé une fois à la construction |
Pkcs11Signer::getCertificateChainDer() | Aucun | Renvoie les intermédiaires fournis au constructeur | array<string> (DER) | Aucun | Exclut le certificat du signataire |
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 | Vérifie proc_open, sonde le binaire et sa version, résout le backend et charge les certificats | — | HsmOperationException (proc_open désactivé, fichier de module/config/certificat manquant, défaillance du binaire, aucun backend) ; InvalidArgumentException (pin-value dans $keyUri) | OpenSslCliBackend::Auto préfère le fournisseur OpenSSL 3.x, puis le moteur |
OpenSslCliSigner::sign() | string $data, string $algorithm = 'sha256WithRSAEncryption' | Exécute openssl dgst dans un sous-processus ; le PIN transite par un fichier pin-source éphémère 0600 par défaut | string octets de signature bruts | HsmOperationException (timeout, PIN rejeté, clé introuvable, échec de chargement du module, sortie vide, échec du fichier pin) ; InvalidArgumentException (algorithme non mappé) ; exceptions du garde-fou FIPS avant la signature | Le sous-processus est tué après $timeoutSeconds ; stderr est expurgé avant d’atteindre les messages |
Surface d’accesseurs OpenSslCliSigner | Aucun | Résultats de construction en lecture seule | string / array<string> / OpenSslCliBackend | Aucun | getCertificateDer, getCertificateChainDer, getPublicKeyAlgorithm, getCertificatePem, getResolvedBackend, getOpensslVersion |
HsmSignerProviderAdapter::__construct() | HsmSignerInterface $hsm, string $providerId, SignatureAlgorithm $algorithm = SignatureAlgorithm::Pkcs1v15 | Enveloppe un concret HSM comme un SignerProviderInterface | — | Aucun | Conventions d’id de fournisseur : pkcs11-{module-id}, openssl-cli |
HsmSignerProviderAdapter::providerId() | Aucun | Renvoie l’id fourni au constructeur | non-empty-string | Aucun | — |
HsmSignerProviderAdapter::supportsAlgorithm() | SignatureAlgorithm $algo | Mappe l’enum vers un nom de style OpenSSL, puis intersecte l’ensemble autorisé du backend | bool | Aucun | Décline les algorithmes digest-only ; les id openssl-engine n’annoncent rien |
HsmSignerProviderAdapter::sign() | string $data, ?string $keyVersion = null | Dispatche à travers le signataire enveloppé avec l’algorithme configuré | non-empty-string | KeyManagementException ($keyVersion non nul) ; SignatureFailedException (algorithme non mappable, défaillance du pilote, signature vide) | Contrat SPI fail-closed ; chaque erreur de pilote remonte typée |
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): stringContrat de comportement
Section intitulée « Contrat de comportement »- Garde des clés. La clé privée ne quitte jamais la frontière du jeton.
Pkcs11Signerdélègue l’opération au jeton ;OpenSslCliSignerpasse une référence de clé — une URI PKCS#11 — au sous-processusopenssl. Aucun des deux signataires ne peut exporter la clé. - Session et connexion.
Pkcs11Signermet en cache un handle de module PKCS#11 par chemin de bibliothèque par processus, car l’interface du jeton doit être initialisée exactement une fois par processus. Chaque opération ouvre une session et se connecte avec le PIN ; la connexion authentifie l’utilisateur avant toute utilisation de clé privée (PKCS#11 v3.1 §5.6.8). Lorsque le slot signale une connexion existante, le signataire se déconnecte puis se reconnecte, afin que les jetons exigeant un PIN frais par opération en reçoivent un. - Ensemble d’algorithmes (fermé). Les deux signataires acceptent exactement :
sha256WithRSAEncryption,sha384WithRSAEncryption,sha512WithRSAEncryption;RSASSA-PSS,RSASSA-PSS-SHA256,RSASSA-PSS-SHA384,RSASSA-PSS-SHA512;ecdsa-with-SHA256,ecdsa-with-SHA384,ecdsa-with-SHA512.Pkcs11Signeraccepte en plusecdsa-raw. Tout autre identifiant lèveInvalidArgumentException— aucun algorithme de substitution n’est jamais signé. - Liaison du sel PSS. Pour chaque variante PSS, la longueur du sel est égale à la longueur du condensat — 32, 48 ou 64 octets — et les paramètres de hachage et de MGF correspondent au condensat choisi. Cela suit la structure des paramètres de mécanisme PSS, où la longueur du sel est typiquement la longueur du hachage du message (PKCS#11 v3.1 §6.1.9). Les deux signataires appliquent le même appariement, de sorte qu’une configuration valide sur un backend est valide sur l’autre.
- Conversion ECDSA. Un jeton renvoie une signature ECDSA comme la concaténation brute, zéro-remplie, de r et s (PKCS#11 v3.1 §6.3.1).
Pkcs11Signer::sign()convertit cette sortie vers la formeECDSA-Sig-Valueencodée en DER que les validateurs PDF et OpenSSL attendent. L’appelant ne manipule jamais la forme brute. - Livraison du PIN (voie CLI). Dans le comportement par défaut sécurisé, le PIN est écrit dans un fichier éphémère créé exclusivement avec des permissions réservées au propriétaire, référencé via l’attribut
pin-sourcede l’URI PKCS#11, et supprimé après la fin du sous-processus. Dans ce mode, le PIN n’est pas placé dans la ligne de commande ni exporté dans l’environnement du sous-processus. Avec$legacyPinDelivery = true, le PIN est intégré commepin-valuedans l’URI, ce qui est observable dans la ligne de commande du processus ; ce mode est opt-in uniquement. - Discipline du sous-processus.
OpenSslCliSignerlance le binaire avec un tableau d’arguments — sans interpolation par le shell — applique$timeoutSeconds, tue le sous-processus à l’expiration et classe stderr en erreurs typées. Les secrets sont expurgés de stderr avant qu’il ne soit cité dans un message d’exception. - Sémantique de l’adaptateur. Un jeton HSM n’a aucun concept de version de clé gérée ; la clé sur le jeton est la version.
HsmSignerProviderAdapter::sign()rejette donc tout$keyVersionnon nul avecKeyManagementExceptionau lieu de l’ignorer.supportsAlgorithm()intersecte le mappage de l’enum avec l’ensemble accepté du backend enveloppé, de sorte que l’adaptateur n’annonce jamais un mécanisme que le backend rejetterait au moment de la signature. Une signature vide provenant du pilote lèveSignatureFailedException. - Aperçu post-quantique.
signPqs()est protégé par le drapeau de constructeur$enablePostQuantumet refuse de s’exécuter autrement. La chaîne de contexte est limitée à 255 octets, correspondant à la borne de contexte ML-DSA (FIPS 204). La signature renvoyée doit correspondre à la longueur exacte en octets du jeu de paramètresPkcs11PqsAlgorithmsélectionné, sinon l’appel échoue. Les identifiants de mécanisme suivent une extension PQ PKCS#11 provisoire et ne sont pas définitifs. Les profils PAdES ne reconnaissent pas les suites post-quantiques, la plupart des validateurs PDF rejettent de telles signatures, et NextPDF ne fournit aucune voie de vérification pour elles. Aucune conformité n’est revendiquée.
Cas limites et modes de défaillance
Section intitulée « Cas limites et modes de défaillance »- Construire
Pkcs11Signersansext-pkcs11lèveHsmOperationExceptionimmédiatement ; l’extension n’est pas fournie avec les distributions PHP standard. - Un label de certificat ou de clé privée qui ne correspond à aucun objet sur le jeton lève
HsmOperationExceptionen nommant la classe d’objet manquante. Le label de clé peut légitimement différer du label de certificat sur certains jetons. - Des connexions échouées répétées peuvent verrouiller le PIN au niveau du jeton ; c’est le jeton qui applique cette politique, pas NextPDF. Les jetons dont les clés exigent une authentification à chaque utilisation reçoivent une connexion fraîche via la voie déconnexion-puis-réessai (PKCS#11 v3.1, sémantique always-authenticate).
OpenSslCliSignerrefuse à la construction un$keyUriqui contient déjàpin-value, en mode fail-closed, car cette livraison contournerait la voie de PIN sécurisée.- Sous Windows, le mode de fichier pin sécurisé échoue de manière fail-closed avec
HsmOperationException: les bits de permission de fichier ne peuvent pas y restreindre les octrois de lecture ACL, de sorte que le signataire refuse de laisser un PIN en clair à la merci des ACL du répertoire temporaire. La livraison de PIN legacy est l’alternative documentée, opt-in, pour les hôtes Windows de confiance. - La détection automatique du backend requiert OpenSSL 3.x pour la voie du fournisseur ; LibreSSL ne résout jamais vers le fournisseur. Lorsque ni une sonde de fournisseur ni une sonde de moteur ne réussit, la construction échoue avec
HsmOperationExceptionplutôt que de reporter l’échec au moment de la signature. - Un sous-processus qui dépasse
$timeoutSecondsest terminé et signalé comme un timeout ; un sous-processus qui se termine proprement avec une sortie vide est signalé comme un échec de signature vide. Aucune de ces conditions ne peut produire un document partiellement signé. - Une signature post-quantique dont la longueur en octets ne correspond pas au jeu de paramètres sélectionné est rejetée avant qu’elle ne puisse atteindre l’encodage CMS.
HsmSignerProviderAdapteravec l’id de fournisseur retiréopenssl-enginen’annonce aucun algorithme, de sorte qu’une configuration périmée échoue à la sélection du fournisseur plutôt qu’au moment de la signature.
Comportement en mode FIPS
Section intitulée « Comportement en mode FIPS »Les deux signataires acceptent un FipsSignatureEnforcer optionnel. Lorsqu’il est câblé, le mode FIPS est actif pour ce signataire : sign() rejette un algorithme de signature non autorisé ou une clé sous le seuil avant toute signature par le jeton ou le sous-processus. Les seuils suivent la table de génération de signature — les modules RSA sous 2048 bits et les ordres ECDSA sous 224 bits sont interdits (NIST SP 800-131A Rev.2 §3 Table 2). Sans enforcer, le comportement est inchangé. Le garde-fou couvre uniquement la voie classique sign() ; signPqs() est régi par son propre drapeau d’aperçu. Ce sont des revendications de capacité concernant le code NextPDF : la validation FIPS 140-3 s’attache à un module cryptographique via le CMVP, qui dans ce déploiement est le HSM ou le fournisseur de l’opérateur — NextPDF n’est pas un module validé, ne détient aucune certification et n’en accorde aucune.
Conformité
Section intitulée « Conformité »| Revendication | Standard | Clause |
|---|---|---|
| La connexion authentifie l’utilisateur auprès du jeton avant les opérations de clé privée ; un mauvais PIN refuse l’accès. | PKCS#11 v3.1 | §5.6.8 |
| Les clés always-authenticate exigent une connexion fraîche à chaque utilisation ; une ré-authentification échouée à répétition peut verrouiller le PIN. | PKCS#11 v3.1 | CKA_ALWAYS_AUTHENTICATE re-authentication |
| Une signature ECDSA de jeton est la concaténation brute r‖s ; le signataire la convertit en DER pour l’interopérabilité PDF. | PKCS#11 v3.1 | §6.3.1 |
| Les paramètres PSS lient le hachage, le MGF et la longueur du sel ; les signataires fixent le sel égal à la longueur du condensat. | PKCS#11 v3.1 | §6.1.9 |
| Le garde-fou FIPS refuse la génération de signature avec RSA sous 2048 bits ou un ordre ECDSA sous 224 bits. | NIST SP 800-131A Rev.2 | §3 Table 2 |
| La chaîne de contexte post-quantique est limitée à 255 octets. | FIPS 204 | HashML-DSA context handling |
| La validation FIPS 140-3 s’attache aux modules cryptographiques via le CMVP. | FIPS 140-3 | CMVP program scope |
Toutes les clauses sont paraphrasées ; aucun texte normatif n’est reproduit. NextPDF ne formule aucune revendication de certification. Les signataires alignent leur comportement sur les clauses citées à titre de capacité. Qu’une signature produite se vérifie est la décision du vérificateur au regard de ses ancres de confiance ; la sécurité de la clé dépend du jeton, du HSM et de l’opérateur — pas de NextPDF seul.
Notes de développement
Section intitulée « Notes de développement »-
Le mécanisme de livraison du PIN suit la convention
pin-sourcede l’URI PKCS#11 (RFC 7512) ; cette RFC est hors du corpus cité, de sorte que le comportement ci-dessus est fondé sur la source du produit, pas sur une citation de spécification. -
Confirme que l’environnement d’exécution charge
ext-pkcs11avant de construirePkcs11Signer; la construction échoue rapidement lorsque l’extension est absente. Le signataire CLI a besoin deproc_openactivé et d’un binaireopensslavec un fournisseur ou un moteur PKCS#11 installé. -
Le PIN, le label de certificat et le label de clé sont
#[SensitiveParameter], de sorte qu’ils sont exclus des traces de pile. Fournis le PIN depuis un gestionnaire de secrets ; ne l’écris jamais dans le code source, dans une configuration commise au contrôle de version, ni dans les logs. -
La construction est l’étape coûteuse sur les deux signataires : la voie PKCS#11 se connecte et lit le certificat, et la voie CLI sonde le binaire et le backend. Construis une fois et réutilise l’instance ; le cache de module par bibliothèque rend sûre une construction répétée contre la même bibliothèque.
-
Enveloppe un signataire dans
HsmSignerProviderAdapterlorsque l’appelant travaille à traversSignerProviderInterface. Passe l’id de fournisseur canonique pour la classe enveloppée —pkcs11-{module-id}ouopenssl-cli— afin que les vérifications de capacité utilisent le bon ensemble autorisé du backend. -
Avant d’activer l’aperçu post-quantique, vérifie les identifiants de mécanisme du firmware du jeton par rapport aux valeurs provisoires que NextPDF enregistre ; une incohérence échoue au moment de la signature. N’active pas l’aperçu pour une sortie PAdES de production.
-
getResolvedBackend()etgetOpensslVersion()existent pour l’enregistrement de preuves ; persiste-les avec les preuves de signature lorsque ton programme de conformité exige la reproductibilité.
Voir aussi
Section intitulée « Voir aussi »- Signature par module de sécurité matériel (PKCS#11) — la page de capacité avec les étapes de mise en place, de configuration et de vérification.
- Sécurité — Référence détaillée — la surface de sécurité Enterprise combinée.
- Signature — Référence détaillée — le producteur PAdES B-LT / B-LTA de longue durée.
- FIPS 140 — Référence détaillée — la politique cryptographique, la batterie d’auto-tests et le garde-fou
FipsSignatureEnforcer. - Aperçu PQC — Référence détaillée — la surface d’aperçu post-quantique et ses limites.
- Sécurité / Signature (Core) — le signataire CMS Core et les contrats de signature.
Frontière de publication
Section intitulée « Frontière de publication »Cette page documente uniquement le comportement observable de l’extérieur et la surface d’API publique prise en charge. Les chemins d’espaces de noms internes, les classes utilitaires, les tables de mécanismes, les noms de fichiers de runbook et les préfixes de tickets sont hors périmètre.