💡 Voll funktionsfähiges Beispiel auf GitHub verfügbar:
sign-docx-with-mldsa-certificates-python

Einführung

Unterschreiben Sie heute Nachmittag einen Vertrag mit RSA‑2048 und Sie geben ein Versprechen ab, das so lange gelten muss, wie der Vertrag von Bedeutung ist. Wenn das zwanzig oder dreißig Jahre sind – und bei Urkunden, Einverständniserklärungen und technischen Freigaben häufig der Fall ist – muss das Versprechen den Algorithmus überdauern. Der Angriff muss nicht heute existieren. Er muss existieren, bevor das Dokument seine Relevanz verliert, und dann kann jeder, der den öffentlichen Schlüssel besitzt, den privaten Schlüssel ableiten und in Ihrem Namen unterschreiben.

Post‑quantum Dokumenten‑Signatur ist das GroupDocs.Signature‑Feature für Python, das dieses Versprechen durch eines auf ML‑DSA ersetzt, dem von NIST als FIPS 204 im Jahr 2024 standardisierten Signaturalgorithmus. Die Unterstützung des Word‑Formats kam in GroupDocs.Signature 26.9 und nutzt dieselbe API, die Sie bereits kennen: Ein ML‑DSA‑Schlüssel liegt in einer PFX und wird in DigitalSignOptions genau wie ein RSA‑Schlüssel verwendet.

Dieser Leitfaden signiert ein DOCX in vier Schritten, vergleicht die drei Sicherheitsstufen anhand gemessener Ausgaben, prüft die Signatur ausschließlich mit einem öffentlichen Zertifikat und schließt mit den beiden Grenzen, die Sie vor der Umsetzung kennen sollten.

Warum das wichtiger ist als die übliche Migration

Die Signatur‑Migration unterscheidet sich von der Verschlüsselungs‑Migration in einem Aspekt, der es einfacher macht, sie aufzuschieben, und gleichzeitig umständlicher zu beheben.

Bei der Verschlüsselung ist das „Jetzt ernten – später entschlüsseln“-Problem sofort präsent: Alles, was heute abgefangen wird, kann gespeichert und später geöffnet werden. Bei Signaturen wird nichts, das Sie bereits unterschrieben haben, nachträglich fälschbar – aber nichts, das Sie unterschrieben haben, bleibt provizierend Ihnen zugeordnet, sobald der Schlüssel aus dem Zertifikat, das jeder kopieren kann, abgeleitet werden kann. Das erneute Signieren eines Jahrzehnts archivierter Dokumente mit neuen Schlüsseln ist möglich, und niemand will die Person sein, die das plant.

Deshalb ist der praktische Rat eher eng gefasst als pauschal: Migrieren Sie die Dokumente, deren Aufbewahrungsfrist lang ist, lassen Sie den Rest. Einige Profile haben bereits die Messlatte gesetzt – CNSA 2.0 verlangt ML‑DSA‑87 für Systeme von nationaler Sicherheit – und für alle anderen ist der entscheidende Faktor, wie lange die Datei verteidigungsfähig bleiben muss.

Voraussetzungen

  • Python 3.9 oder neuer auf einem 64‑Bit‑Interpreter – das Paket liefert eine gebündelte .NET‑Runtime und hat kein 32‑Bit‑Wheel
  • GroupDocs.Signature für Python via .NET 26.10.0, mit einer kostenlosen temporären Lizenz, um die Evaluationsbeschränkungen zu entfernen
  • Ein ML‑DSA‑Zertifikat als passwortgeschützte PFX und ein Word‑Dokument zum Signieren

Installation

pip install groupdocs-signature-net

Schritt 1 – Signieren mit einem ML‑DSA‑Zertifikat

Das Zertifikat erledigt die Arbeit. Der Aufruf ist derselbe wie bei RSA:

with signature.Signature(source_path) as sign:
    options = DigitalSignOptions(pfx_path)
    options.password = CERTIFICATE_PASSWORD

    result = sign.sign(output_path, options)

Das ist die gesamte Umstellungsgeschichte für Code, der bereits signiert: DigitalSignOptions auf eine andere PFX zeigen. Keine neue Option, kein separater Algorithmus‑Parameter, kein Zweig für post‑quantum.

Das Auslesen des Signierers erfordert einen weiteren Schritt und enthält die eine Python‑spezifische Falle in dieser gesamten Übung:

for created in result.succeeded:
    certificate = getattr(created, "certificate", None)
    subject = getattr(certificate, "subject", None)
    if subject:
        return str(subject)

Das Zertifikat auf einer DigitalSignature ist ein Brückenobjekt, das Attribute dynamisch auflöst. certificate.subject liefert CN=GroupDocs.Signature MLDSA65 test, während dir() auf demselben Objekt überhaupt nichts auflistet. Ich habe es zuerst mit dir() inspiziert, geschlossen, dass das Subject nicht exponiert sei, und lag damit einfach falsch – wenn Sie also introspektieren, bevor Sie lesen, überspringen Sie einen vorhandenen Wert.

Schritt 2 – Vergleich der drei Sicherheitsstufen

ML‑DSA gibt es in drei Parametersätzen, die durch Übergabe eines anderen Zertifikats ausgewählt werden:

levels = (
    ("ML-DSA-44", MLDSA44_PFX),
    ("ML-DSA-65", MLDSA65_PFX),
    ("ML-DSA-87", MLDSA87_PFX),
)

for level, pfx_path in levels:
    with signature.Signature(source_path) as sign:
        options = DigitalSignOptions(pfx_path)
        options.password = CERTIFICATE_PASSWORD
        sign.sign(output_path, options)

    sizes[f"{level} -> {file_name}"] = os.path.getsize(output_path)

Dies ist der Schritt, der sich tatsächlich lohnt, auszuführen, weil der Kompromiss normalerweise beschrieben, aber selten gemessen wird. Aus einem 132 KB‑Quellvertrag:

Stufe NIST Sicherheitskategorie Signierte Datei Über die kleinste
ML-DSA-44 2 138,202 Bytes -
ML-DSA-65 3 140,650 Bytes +2,448 Bytes
ML-DSA-87 5 143,971 Bytes +5,769 Bytes

Unter 6 KB trennt die schwächste Stufe von der stärksten. Bei einem Vertrag ist das nichts, was die Entscheidung vereinfacht: Verwenden Sie ML‑DSA‑65 als Standard, ML‑DSA‑87 dort, wo ein Profil Kategorie 5 verlangt oder die Größe irrelevant ist, und ML‑DSA‑44 nur, wenn Sie so viele Dateien signieren, dass Kilobytes zu etwas Realem aggregieren.

Schritt 3 – Verifizieren mit einem öffentlichen Zertifikat

Ein Empfänger benötigt das öffentliche Zertifikat des Signierers und nichts Geheimes:

with signature.Signature(signed_path) as sign:
    options = DigitalVerifyOptions(certificate_path)
    if password is not None:
        options.password = password

    return sign.verify(options).is_valid

Das Beispiel ruft dies zweimal für dieselbe Datei auf: einmal mit mldsa65.cer, der öffentlichen Hälfte des Signaturschlüssels, und einmal mit einer anderen Signatur‑PFX. Das erste liefert True, das zweite False. Beachten Sie, dass das falsche Zertifikat ein False zurückgibt, anstatt eine Ausnahme zu werfen – „von jemand anderem signiert“ ist eine Antwort, die Ihr Code behandeln sollte, keine Ausnahme. Die Prüfung umfasst den Dokumentinhalt sowie die Seriennummer und den Fingerabdruck des Zertifikats, sodass eine nach der Signatur bearbeitete Datei ebenfalls fehlschlägt.

Schritt 4 – Auslesen der Signaturen aus einem Dokument

Wenn ein signiertes Dokument eintrifft und Sie nicht wissen, welches Zertifikat zu erwarten ist:

with signature.Signature(signed_path) as sign:
    found = sign.search(SignatureType.DIGITAL)
    for item in found:
        print(item.sign_time, item.is_valid)

search mit SignatureType.DIGITAL liefert DigitalSignature‑Objekte, die das Zertifikat, die Signaturzeit und ein Gültigkeitsflag enthalten. Ein Word‑Dokument kann mehrere Signaturen halten, einschließlich einer Mischung aus RSA‑ und ML‑DSA‑Signaturen, und jede wird mit ihrem eigenen Zertifikat und ihrer eigenen Gültigkeit gemeldet.

Ändert das die Art, wie Empfänger verifizieren?

In keiner Weise, die ihnen auffällt. Ein Empfänger benötigt weiterhin nur das öffentliche Zertifikat des Signierers, übergibt es an dieselben DigitalVerifyOptions und erhält wieder einen booleschen Wert. Nichts am Verifizierungsweg ist spezifisch für ML‑DSA. Der eine Ort, an dem der Algorithmus sichtbar wird, ist der Signatur‑Indikator von Microsoft Word, der ML‑DSA möglicherweise noch nicht erkennt, weil das Format keinen standardisierten Bezeichner dafür hat.

Praxisbeispiele

Langfristige Verträge

Der klarste Fall. Ein Dokument, das über Jahrzehnte verifizierbar bleiben muss, wird einmal, jetzt, mit ML‑DSA‑65 oder ML‑DSA‑87 signiert und muss nie erneut signiert werden, weil sein Algorithmus nicht mehr veraltet.

Regulierte Umgebungen mit benanntem Profil

Wo CNSA 2.0 oder ein ähnliches Profil gilt, ist die Stufe keine Entscheidung – ML‑DSA‑87 ist die Anforderung, und die einzige technische Frage ist, ob das Format unterstützt wird.

Gemischte Pipelines während der Migration

Neue Dokumente post‑quantum signieren und das Archiv unverändert lassen ist ein völlig akzeptabler Zwischenzustand, und das separate Reporting jeder Signatur durch search macht das handhabbar.

Best Practices und Tipps

  • Nach Aufbewahrungsdauer, nicht nach Volumen migrieren. Die Dokumente, die das benötigen, sind die langlebigen; ein Beleg, der nur 90 Tage relevant ist, nicht.
  • Standardmäßig ML‑DSA‑65 verwenden, es sei denn, ein Profil nennt eine andere Stufe, und nicht über die Größenunterschiede grübeln – sie liegen unter 6 KB pro Signatur.
  • RSA beibehalten, wo der Empfänger in Word validiert. Korrekte Signaturen, die ein Leser als ungültig markiert, sind schlimmer als eine langsamere Migration.
  • Testzertifikate ersetzen. Die PFX‑Dateien des Beispiels sind selbstsigniert mit veröffentlichtem Passwort, sodass alles, was damit signiert wird, nichts beweist.
  • Nach dem Signieren verifizieren in jeder Pipeline, wobei das öffentliche Zertifikat verwendet wird, das ein Empfänger besitzen würde.

Fehlersuche bei häufigen Problemen

Microsoft Word zeigt die Signatur nicht als gültig an. Erwartet für jetzt: Es gibt keinen standardisierten XML‑DSig‑Bezeichner für ML‑DSA, sodass Word ihn möglicherweise nicht erkennt, obwohl die Signatur korrekt ist und GroupDocs.Signature sie verifiziert. Verifizieren Sie in Ihrer eigenen Pipeline und behalten Sie RSA für Dokumente, deren Empfänger auf Word‑Indikatoren angewiesen sind.

Der Signatur‑Aufruf lehnt ein PDF oder eine Tabellenkalkulation ab. ML‑DSA‑Signaturen decken Word‑Formate ab – DOCX, DOC, ODT und weitere. PDF, Tabellen und Präsentationen werden noch nicht unterstützt und müssen weiterhin mit RSA oder ECDSA signiert werden.

Der Zertifikats‑Subject ist leer. Fast immer die dir()‑Falle aus Schritt 1: Das Attribut wird dynamisch aufgelöst, also lesen Sie es, anstatt vorher darauf zu testen.

Fazit

Die Code‑Änderung ist ein Zertifikatswechsel, und genau das macht es lohnenswert, bevor es dringend wird. Signieren Sie die langlebigen Word‑Dokumente mit ML‑DSA‑65, verwenden Sie ML‑DSA‑87, wo ein Profil es verlangt, verifizieren Sie mit dem öffentlichen Zertifikat und behalten Sie RSA, wo das Format oder der Leser es verlangt.

Führen Sie das Beispiel mit einem Ihrer eigenen Verträge aus, und die drei Größen zeigen Ihnen in Bytes genau, was die stärkste verfügbare Stufe kostet. Bei der Datei, die ich getestet habe, waren es 5 769 Bytes.

Weitere Ressourcen