💡 Plně funkční příklad dostupný na GitHubu:
vault-transit-pdf-signing-python

Proč automatizované podepisování selhává při revizi správy klíčů

Týmy pro soulad a finanční operace podepisují tisíce dokumentů: úvěrové smlouvy, výpisy, balíčky politik, auditní dopisy. Téměř žádný z nich není podepsán osobou kliknutím na tlačítko. Služba to provádí podle plánu a certifikát připojený k těmto dokumentům nese název organizace. To činí podpisový klíč jednou z nejcitlivějších věcí, které společnost vlastní, a obvykle leží v souboru PFX vedle aplikace, která jej používá.

Externí podepisování je schopnost GroupDocs.Signature pro Python přes .NET, která předává otisk dokumentu kódu, který poskytnete, místo aby podepisovala soukromým klíčem, který aplikace drží. Existuje, protože uspořádání PFX neodolá přezkoumání. Soubor je kopií a kopie se šíří: do obrazu kontejneru, do nasazovacího pipeline, do zálohy, do notebooku vývojáře, který ladil problém v produkci minulou jaro. Kdokoli, kdo má jednu z těchto kopií a heslo, může podepisovat vaším jménem, dokud si někdo nevšimne a neodvolá certifikát.

Co tyto týmy potřebují, není jiný formát souboru. Potřebují, aby klíč existoval na jediném místě, s auditním záznamem každého jeho použití, a podpisovou službu, která může požádat o podpis, aniž by byla kdykoli důvěřována klíči. K tomu slouží správce klíčů jako HashiCorp Vault a pomůže jen tehdy, pokud knihovna dokumentů může být instruována delegovat kryptografii.

Proč soubor certifikátu chráněný heslem není dostačující

Instinkt je zamknout PFX: omezit oprávnění k souboru, uložit heslo do správce tajemství, šifrovat svazek. Každý krok pomáhá a žádný z nich nemění podstatný fakt. V okamžiku podepisování je soukromý klíč načten do procesu, takže výpis paměti, únik z kontejneru nebo neopatrná logovací zpráva jej stále mohou odhalit. Ovládání, které chcete, je klíč, který nelze vůbec přečíst, ne klíč, který je jen těžší přečíst.

Transit engine Vaultu to poskytuje. Provádí kryptografii jako službu: pošlete hodnotu, on ji podepíše nebo zašifruje svým klíčem a vrátí výsledek. Klíče vytvořené uvnitř mohou být označeny jako neexportovatelné a nikdy neukládá to, co přijme. GroupDocs.Signature pokrývá druhou polovinu, protože DigitalSignOptions.custom_sign_hash vám umožní zapojit vlastní podepisovač do pipeline podepisování PDF.

Nastavení: GroupDocs.Signature pro Python s Vault Transit

GroupDocs.Signature dělá vše až po kryptografii. Otevře PDF, určí, jaký rozsah bajtů podpis pokrývá, zahashuje tento rozsah a zavolá vašeho podepisovače s otiskem. Váš podepisovač předá otisk Vaultu a vrátí bajty, které Vault vytvořil. Knihovna je vloží společně s certifikátem a zapíše normální podepsané PDF.

Potřebujete Python 3.11 nebo 3.13, balíčky groupdocs-signature-net a asn1crypto a Docker pro kontejner Vaultu v režimu vývoje. Licence GroupDocs je volitelná; bez ní režim hodnocení akceptuje dokumenty až do 2 stran. Ukázkový repozitář běží od začátku do konce ve dvou příkazech a stejný kód ukazuje na produkční Vault změnou dvou proměnných prostředí.

Vytvoření krok za krokem

Krok 1 – Načtení podpisového klíče z Vaultu

Aplikace požádá Vault o klíč, který je oprávněna použít, a získá zpět číslo verze a veřejný klíč. Soukromá polovina není nikdy součástí odpovědi.

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}")

Čtení verze jednou je smyslem tohoto kroku. Certifikát obsahuje jeden veřejný klíč, takže certifikát a každý podpis musí pocházet ze stejné verze.

Krok 2 – Vytvoření certifikátu pro klíč, který nikdo nemůže exportovat

PDF podpis nese certifikát X.509, aby jej ověřovatel mohl najít veřejný klíč. U neexportovatelného klíče je certifikát sestaven kolem veřejné poloviny a podepsán Vaultem.

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,
})

Certifikát je úmyslně nastaven o den dříve než nyní. PDF čtečka porovnává čas podpisu s tímto počátečním datem a na stroji dvě hodiny východně od UTC byl certifikát vytvořený pár sekund před podpisem Adobe Acrobatem hlášen jako prošlý nebo ještě neplatný.

Krok 3 – Podepsání PDF pomocí externího podepisovače

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)

Dvě řádky jsou specifické pro Python. Vazba nemá typ X.509, takže objekt certifikátu přichází zpět z DigitalVerifyOptions; předání cesty do DigitalSignOptions místo toho vyvolá chybu „Specified certificate doesn’t contain private key“. A sign_time je úmyslně časově zónově vědomý, protože hodnota s posunem je do PDF zapsána.

Uvnitř podepisovače jde otisk do Vaultu s prehashed = true a pkcs1v15. Tyto dva pole rozhodují, zda je podpis platný, a první selže tiše, když chybí.

Krok 4 – Ověření před tím, než něco opustí službu

options = DigitalVerifyOptions(certificate_stream=io.BytesIO(certificate))
with gs.Signature(str(signed_pdf)) as signature:
    is_valid = signature.verify(options).is_valid

Podepisování a ověřování ve stejném běhu převádí tichý režim selhání na okamžité. Pro pipeline dokumentů je to brána: soubor, který neprojde ověřením, nikdy nedojde do archivu, ke kontrahentovi ani do zákaznického portálu.

Obchodní dopad: Co se mění pro audit

Obava PFX na hostiteli aplikace Podepisování přes Vault Transit
Kopie soukromého klíče jedna na hostitele, obraz, zálohu a notebook jedna, uvnitř Vaultu
Práva, která podpisová aplikace potřebuje plný přístup k souboru s klíčem číst klíč, volat transit/sign
Důkaz, že dokument byl podepsán logy aplikace, pokud je někdo přidal jedna auditní položka Vaultu na podpis
Reakce na podezření na únik odvolat, znovu vydat, nasadit všude otočit klíč ve Vaultu, jednou znovu vydat
Kdo může podepisovat vaším jménem kdokoli se souborem a heslem kdokoli s platným tokenem Vaultu

Poslední řádek je upřímný. Tento návrh přesouvá tajemství místo jeho odstranění: odcizený token Vaultu může stále požadovat podpisy. Rozdíl je v tom, že token je krátkodobý, odvolatelný na jednom místě, omezený na dvě operace a každé jeho použití je zaznamenáno. To je podstatně snazší bránit při revizi než soubor, jehož kopie nikdo nedokáže spočítat.

Skutečný pracovní postup: úvěrová smlouva podepsaná službou

Úvěrová platforma vygeneruje PDF smlouvu, když je žádost schválena, podepíše ji a uloží do úložiště dokumentů zákazníka. Před touto změnou držela podpisová služba firemní certifikát a nasazovací runbook měl krok pro kopírování PFX na nové hosty. Po změně služba drží token AppRole se dvěma oprávněními a soukromý klíč certifikátu nikdy neexistoval nikde kromě Vaultu.

Když auditoři požádají, kolik dokumentů bylo podepsáno za poslední čtvrtletí a kým, odpověď pochází z auditního zařízení Vaultu místo rekonstruování logů aplikace. Když tým otočí klíč, otočí jej na jednom místě a vydá nový certifikát pro novou verzi.

Co bych měl nejprve udělat správně?

Zpracování otisku. Špatný název algoritmu selže hlasitě, Vault odpoví
500 crypto/rsa: input must be hashed message a stojí to minutu. Chybějící příznak prehashed nevygeneruje žádnou chybu: Vault otisk znovu zahashuje, podpis pokrývá špatnou hodnotu a PDF vypadá dokončeně. Ověřujte každý soubor, který podepisujete, a toto selhání přestane být neviditelné.

Další místa, kde tento návrh zapadá

Hook neví, co je za ním, takže stejné čtyři kroky fungují proti hardwarovému bezpečnostnímu modulu, cloudovému KMS nebo chytré kartě. Přenesl jsem tuto implementaci ze svého .NET sourozence s očekáváním, že podepisovač bude potřebovat přepracování, a jedinou částí, která se skutečně změnila, byl předání certifikátu; tělo požadavku Vaultu a připínání verze byly identické. Týmy ve zdravotnictví, vládě a pojišťovnictví jej používají ze stejného důvodu jako finance: klíč musí být na místě, které dokáže prokázat, k čemu byl použit.

Začínáme

Zdroje