💡 Exemple complet disponible sur GitHub : vault-transit-pdf-signing-python
Pourquoi la signature automatisée échoue à un examen de garde de clé
Les équipes de conformité et des opérations financières signent des documents par milliers : contrats de prêt, relevés, packs de politiques, lettres d’audit. Presque aucun de ces documents n’est signé par une personne cliquant sur un bouton. Un service le fait, selon un planning, et le certificat joint à ces documents porte le nom de l’organisation. Cela fait de la clé de signature l’un des éléments les plus sensibles que possède l’entreprise, et elle se trouve généralement dans un fichier PFX à côté de l’application qui l’utilise.
La signature externe est une capacité de GroupDocs.Signature pour Python via .NET qui transmet le condensé du document à du code que vous fournissez au lieu de signer avec une clé privée détenue par l’application. Elle existe parce que le dispositif PFX ne résiste pas à l’examen. Un fichier est une copie, et les copies se propagent : dans l’image du conteneur, le pipeline de déploiement, la sauvegarde, l’ordinateur portable du développeur qui a débogué un problème de production le printemps dernier. Quiconque possède l’une de ces copies et le mot de passe peut signer en votre nom jusqu’à ce que quelqu’un remarque et révoque le certificat.
Ce dont ces équipes ont besoin n’est pas un format de fichier différent. Elles ont besoin que la clé n’existe qu’à un seul endroit, avec une piste d’audit à chaque utilisation, et un service de signature qui peut demander une signature sans jamais être en possession de la clé. C’est à cela qu’un gestionnaire de clés comme HashiCorp Vault sert, et cela ne fonctionne que si la bibliothèque de documents peut être configurée pour déléguer la cryptographie.
Pourquoi un fichier de certificat protégé par mot de passe n’est pas suffisant
L’instinct est de verrouiller le PFX : restreindre les permissions du fichier, stocker le mot de passe dans un gestionnaire de secrets, chiffrer le volume. Chaque étape aide et aucune d’elles ne change le fait fondamental. Au moment de la signature, la clé privée est chargée dans le processus, donc un vidage de mémoire, une fuite de conteneur ou une instruction de journalisation négligente peuvent encore l’exposer. Le contrôle que vous voulez est une clé qui ne peut pas être lue du tout, pas une clé qui est simplement plus difficile à lire.
Le moteur Transit de Vault fournit cela. Il effectue la cryptographie en tant que service : vous envoyez une valeur, il signe ou chiffre avec une clé qu’il détient, et renvoie le résultat. Les clés créées à l’intérieur peuvent être marquées comme non exportables, et il ne stocke jamais ce qu’il reçoit. GroupDocs.Signature couvre l’autre moitié, car DigitalSignOptions.custom_sign_hash vous permet d’intégrer votre propre signataire dans le pipeline de signature PDF.
Configuration : GroupDocs.Signature pour Python avec Vault Transit
GroupDocs.Signature fait tout jusqu’à la cryptographie. Il ouvre le PDF, détermine quelle plage d’octets la signature couvre, calcule le hachage de cette plage, et appelle votre signataire avec le condensé. Votre signataire transmet le condensé à Vault et renvoie les octets produits par Vault. La bibliothèque les intègre avec le certificat et écrit un PDF signé normal.
Vous avez besoin de Python 3.11 ou 3.13, des packages groupdocs-signature-net et asn1crypto, et de Docker pour un conteneur Vault en mode développement. Une licence GroupDocs est optionnelle ; sans licence, le mode d’évaluation accepte les documents jusqu’à 2 pages. Le dépôt d’exemple s’exécute de bout en bout en deux commandes, et le même code pointe vers un Vault de production en modifiant deux variables d’environnement.
Construction étape par étape
Étape 1 – Lire la clé de signature depuis Vault
L’application demande à Vault la clé qu’elle est autorisée à utiliser, et reçoit un numéro de version ainsi qu’une clé publique. La moitié privée ne fait jamais partie de la réponse.
key = vault.read_key(KEY_NAME)
if key is None:
# First run: enable Transit and create an RSA-2048 key.
vault.enable_transit()
vault.create_key(KEY_NAME, "rsa-2048")
key = vault.read_key(KEY_NAME)
print(f"Signing key: '{KEY_NAME}', version {key.version}")
Lire la version une fois est le but de cette étape. Un certificat ne contient qu’une clé publique, donc le certificat et chaque signature doivent provenir de la même version.
Étape 2 – Créer un certificat pour une clé que personne ne peut exporter
Une signature PDF porte un certificat X.509 afin qu’un vérificateur puisse trouver la clé publique. Avec une clé non exportable, le certificat est assemblé autour de la moitié publique et signé par Vault.
tbs_certificate = x509.TbsCertificate({...}) # built around the Vault public key
# Hash locally, then have Vault sign the certificate's own digest.
digest = hashlib.sha256(tbs_certificate.dump()).digest()
signature = vault.sign_hash(key_name, key.version, digest, "sha2-256")
certificate = x509.Certificate({
"tbs_certificate": tbs_certificate,
"signature_algorithm": algorithm,
"signature_value": signature,
})
Le certificat commence délibérément un jour avant maintenant. Un lecteur PDF compare l’heure de signature à cette date de début, et sur une machine deux heures à l’est de UTC un certificat créé quelques secondes avant la signature a été signalé par Adobe Acrobat comme expiré ou pas encore valide.
Étape 3 – Signer le PDF via le signataire externe
certificate_object = DigitalVerifyOptions(
certificate_stream=io.BytesIO(certificate)).certificate
digital_signature = DigitalSignature(certificate=certificate_object)
digital_signature.sign_time = datetime.now().astimezone()
options = DigitalSignOptions()
options.signature = digital_signature
options.custom_sign_hash = VaultSigner(vault, KEY_NAME, key.version)
options.hash_algorithm = HashAlgorithm.SHA256
options.reason = "Document signed with HashiCorp Vault Transit"
with gs.Signature(str(input_pdf)) as signature:
signature.sign(str(output_pdf), options)
Deux lignes sont spécifiques à Python. Le binding n’a pas de type X.509, donc l’objet certificat ressort de DigitalVerifyOptions ; fournir un chemin à DigitalSignOptions déclenche l’erreur « Specified certificate doesn’t contain private key ». Et sign_time est conscient du fuseau horaire exprès, car une valeur avec décalage est écrite dans le PDF.
À l’intérieur du signataire, le condensé est envoyé à Vault avec prehashed = true et pkcs1v15. Ces deux champs décident si la signature est valide, et le premier échoue silencieusement lorsqu’il est absent.
Étape 4 – Vérifier avant que quoi que ce soit ne quitte le service
options = DigitalVerifyOptions(certificate_stream=io.BytesIO(certificate))
with gs.Signature(str(signed_pdf)) as signature:
is_valid = signature.verify(options).is_valid
Signer et vérifier dans la même exécution transforme le mode d’échec silencieux en un échec immédiat. Pour un pipeline de documents, c’est le filtre : un fichier qui ne se vérifie pas n’atteint jamais l’archive, le partenaire ou le portail client.
Impact commercial : ce qui change pour l’audit
| Préoccupation | PFX sur l’hôte de l’application | Signature via Vault Transit |
|---|---|---|
| Copies de la clé privée | une par hôte, image, sauvegarde et ordinateur portable | une, dans Vault |
| Droits nécessaires à l’application de signature | accès complet au fichier de clé | lecture de la clé, appel de transit/sign |
| Preuve qu’un document a été signé | journaux d’application, si quelqu’un les a ajoutés | une entrée d’audit Vault par signature |
| Réponse à une fuite suspectée | révoquer, réémettre, redéployer partout | faire pivoter la clé dans Vault, réémettre une fois |
| Qui peut signer en votre nom | quiconque possède le fichier et le mot de passe | quiconque détient un token Vault valide |
La dernière ligne est la sincère. Cette conception déplace le secret plutôt que de le supprimer : un token Vault volé peut encore demander des signatures. La différence est qu’un token est de courte durée, révoquable en un seul endroit, limité à deux opérations, et chaque utilisation est journalisée. C’est nettement plus facile à défendre lors d’un examen qu’un fichier dont les copies ne peuvent être énumérées.
Flux de travail réel : un accord de prêt signé par un service
Une plateforme de prêt génère un PDF d’accord lorsqu’une demande est approuvée, le signe, puis le dépose dans le coffre de documents du client. Avant ce changement, le service de signature détenait le certificat de l’entreprise et le manuel de déploiement comportait une étape de copie du PFX sur les nouveaux hôtes. Après, le service possède un token AppRole avec deux permissions, et la clé privée du certificat n’a jamais existé ailleurs que dans Vault.
Lorsque les auditeurs demandent combien de documents ont été signés le trimestre dernier et par qui, la réponse provient du dispositif d’audit de Vault plutôt que de la reconstruction des journaux d’application. Lorsque l’équipe fait pivoter la clé, elle le fait en un seul endroit et émet un nouveau certificat pour la nouvelle version.
Qu’est-ce que je devrais bien faire en premier ?
La gestion du digest. Un nom d’algorithme incorrect échoue bruyamment, Vault répondant 500 crypto/rsa: input must be hashed message, et cela coûte une minute. Un drapeau prehashed manquant ne produit aucune erreur : Vault hache à nouveau le digest, la signature couvre la mauvaise valeur, et le PDF semble terminé. Vérifiez chaque fichier que vous signez et cet échec cessera d’être invisible.
Autres contextes où ce design convient
Le crochet n’a aucune idée de ce qui se trouve derrière, donc les mêmes quatre étapes fonctionnent avec un module de sécurité matériel, un KMS cloud ou une carte à puce. J’ai porté cette construction depuis son homologue .NET en m’attendant à ce que le signataire nécessite une refonte, et la seule partie réellement modifiée était le transfert du certificat ; le corps de la requête Vault et le verrouillage de version étaient identiques. Les équipes de santé, de gouvernement et d’assurance l’adoptent pour la même raison que la finance : la clé doit être quelque part qui puisse prouver à quoi elle a servi.
Commencer
- Commencer gratuitement : obtenir une licence d’essai, accès complet à l’API
- Lire le guide d’intégration : signer des PDF avec une clé Vault Transit en Python
- Parcourir le code : vault-transit-pdf-signing-python
- Poser des questions d’implémentation sur le forum communautaire