💡 Pełny działający przykład dostępny na GitHubie: sign-docx-with-mldsa-certificates-python

Wprowadzenie

Podpisz umowę dziś po południu przy użyciu RSA‑2048 i złożysz obietnicę, która musi wytrwać tak długo, jak umowa ma znaczenie. Jeśli to będzie dwadzieścia czy trzydzieści lat – a w przypadku aktów, formularzy zgody i zatwierdzeń inżynieryjnych tak często bywa – obietnica musi przetrwać algorytm. Atak nie musi istnieć już dziś. Musi istnieć zanim dokument przestanie mieć znaczenie, a wtedy każdy posiadający klucz publiczny może wyprowadzić klucz prywatny i podpisać w twoim imieniu.

Podpisywanie dokumentów post‑kwantowych to funkcja GroupDocs.Signature dla Pythona, która zastępuje tę obietnicę taką opartą na ML‑DSA, algorytmie podpisu standaryzowanym przez NIST jako FIPS 204 w 2024 r. Obsługa formatu Word pojawiła się w GroupDocs.Signature 26.9 i ponownie wykorzystuje API, które już masz: klucz ML‑DSA znajduje się w pliku PFX i trafia do DigitalSignOptions dokładnie tak jak klucz RSA.

Ten przewodnik podpisuje plik DOCX w czterech krokach, porównuje trzy poziomy bezpieczeństwa na zmierzonych wynikach, weryfikuje podpis jedynie przy użyciu certyfikatu publicznego i kończy się dwoma ograniczeniami, które warto znać przed podjęciem decyzji.

Dlaczego to ma większe znaczenie niż zwykła migracja

Migracja podpisów różni się od migracji szyfrowania pod jednym względem, który ułatwia jej odkładanie i utrudnia naprawę.

W szyfrowaniu problem „zbierz‑teraz‑odszyfruj‑później” jest natychmiastowy: wszystko przechwycone dziś może być przechowywane i otwarte później. W podpisach nic, co już podpisałeś, nie staje się podrabialne retroaktywnie – ale nic, co podpisałeś, nie pozostaje również dowodem własności, gdy klucz może zostać wyprowadzony z certyfikatu, którego kopię ma każdy. Ponowne podpisanie dekady zarchiwizowanych dokumentów nowymi kluczami jest możliwe i nikt nie chce być osobą, która to planuje.

Dlatego praktyczna rada jest wąska, a nie ogólna: migruj dokumenty, których okres przechowywania jest długi, resztę zostaw. Niektóre profile już wyznaczyły próg – CNSA 2.0 wymaga ML‑DSA‑87 dla systemów o znaczeniu bezpieczeństwa narodowego – a dla wszystkich pozostałych decydującym czynnikiem jest to, jak długo plik musi pozostać obronny.

Wymagania wstępne

  • Python 3.9 lub nowszy na interpreterze 64‑bitowym – pakiet dostarcza wbudowane środowisko .NET i nie posiada koła 32‑bitowego
  • GroupDocs.Signature dla Pythona poprzez .NET 26.10.0, z bezpłatną tymczasową licencją usuwającą ograniczenia ewaluacyjne
  • Certyfikat ML‑DSA w postaci chronionego hasłem pliku PFX oraz dokument Word do podpisania

Instalacja

pip install groupdocs-signature-net

Krok 1 - Podpisz przy użyciu certyfikatu ML‑DSA

Certyfikat wykonuje całą pracę. Wywołanie jest takie samo, jakbyś pisał dla RSA:

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

    result = sign.sign(output_path, options)

To cała historia adopcji dla kodu, który już podpisuje: wskaż DigitalSignOptions na inny PFX. Nie ma nowej opcji, nie ma osobnego parametru algorytmu, nie ma gałęzi dla post‑kwantowego.

Odczytanie podpisującego wymaga jeszcze jednego kroku i zawiera jedną pułapkę specyficzną dla Pythona w całym tym ćwiczeniu:

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

Certyfikat w obiekcie DigitalSignature jest obiektem mostka, który dynamicznie rozwiązuje atrybuty. certificate.subject zwraca CN=GroupDocs.Signature MLDSA65 test, podczas gdy dir() na tym samym obiekcie nie wyświetla nic. Najpierw sprawdziłem to przy pomocy dir(), doszedłem do wniosku, że atrybut nie jest udostępniony, i po prostu się myliłem – więc jeśli introspekcjonujesz przed odczytem, pominiesz wartość, która jest dostępna.

Krok 2 - Porównaj trzy poziomy bezpieczeństwa

ML‑DSA występuje w trzech zestawach parametrów i są one wybierane przez podanie innego certyfikatu:

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)

To krok, który naprawdę warto wykonać, ponieważ kompromis jest zwykle opisywany, a rzadko mierzony. Z pliku źródłowego o wielkości 132 KB:

Poziom Kategoria bezpieczeństwa NIST Podpisany plik Nad najmniejszym
ML-DSA-44 2 138 202 bajtów -
ML-DSA-65 3 140 650 bajtów +2 448 bajtów
ML-DSA-87 5 143 971 bajtów +5 769 bajtów

Mniej niż 6 KB oddziela najsłabszy poziom od najsilniejszego. W przypadku umowy to nic, co upraszcza decyzję: używaj domyślnie ML‑DSA‑65, ML‑DSA‑87 tam, gdzie profil wymaga kategorii 5 lub rozmiar jest nieistotny, a ML‑DSA‑44 tylko wtedy, gdy podpisujesz tak wiele plików, że kilobajty sumują się do czegoś realnego.

Krok 3 - Zweryfikuj przy użyciu certyfikatu publicznego

Odbiorca potrzebuje jedynie publicznego certyfikatu podpisującego i niczego tajnego:

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

Przykład wywołuje to dwukrotnie na tym samym pliku: raz z mldsa65.cer, publiczną połową klucza podpisującego, i raz z innym PFX podpisującego. Pierwsze wywołanie zwraca True, drugie False. Zwróć uwagę, że niewłaściwy certyfikat daje False zamiast podnieść wyjątek – „podpisany przez kogoś innego” to odpowiedź, którą Twój kod powinien obsłużyć, a nie wyjątek. Sprawdzenie obejmuje treść dokumentu wraz z numerem seryjnym i odciskiem palca certyfikatu, więc plik edytowany po podpisaniu również nie przejdzie weryfikacji.

Krok 4 - Odczytaj podpisy z dokumentu

Gdy przychodzi podpisany dokument i nie wiesz, którego certyfikatu się spodziewać:

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

search z SignatureType.DIGITAL zwraca obiekty DigitalSignature zawierające certyfikat, czas podpisu i flagę ważności. Dokument Word może zawierać kilka podpisów, w tym mieszankę RSA i ML‑DSA, a każdy jest raportowany z własnym certyfikatem i własną ważnością.

Czy to zmienia sposób weryfikacji przez odbiorców?

W żaden sposób, którego nie zauważą. Odbiorca nadal potrzebuje jedynie publicznego certyfikatu podpisującego, nadal przekazuje go do tego samego DigitalVerifyOptions i nadal otrzymuje wartość logiczną. Nic w ścieżce weryfikacji nie jest specyficzne dla ML‑DSA. Jedynym miejscem, w którym algorytm się ujawnia, jest wskaźnik podpisu w Microsoft Word, który może jeszcze nie rozpoznawać ML‑DSA, ponieważ format nie ma standardowego identyfikatora dla tego algorytmu.

Zastosowania w praktyce

Umowy o długim okresie przechowywania

Najbardziej oczywisty przypadek. Dokument, który musi pozostać weryfikowalny przez dziesięciolecia, jest podpisany raz, teraz, przy użyciu ML‑DSA‑65 lub ML‑DSA‑87 i nigdy nie wymaga ponownego podpisywania, ponieważ jego algorytm nie starzeje się.

Środowiska regulowane z określonym profilem

Tam, gdzie obowiązuje CNSA 2.0 lub podobny profil, poziom nie jest kwestią oceny – wymóg to ML‑DSA‑87, a jedynym inżynieryjnym pytaniem jest, czy format jest obsługiwany.

Mieszane pipeline’y podczas migracji

Podpisywanie nowych dokumentów post‑kwantowo, pozostawiając archiwum w spokoju, to w pełni uzasadniony stan pośredni, a raportowanie każdej podpisu osobno przez search czyni to zarządzalne.

Najlepsze praktyki i wskazówki

  • Migruj według okresu przechowywania, nie według wolumenu. Dokumenty, które tego potrzebują, to te o długim czasie życia; paragon ważny 90 dni nie wymaga migracji.
  • Domyślnie używaj ML‑DSA‑65, chyba że profil określa inny poziom, i nie przejmuj się różnicą w rozmiarze – to mniej niż 6 KB na podpis.
  • Zachowaj RSA tam, gdzie odbiorca weryfikuje w Wordzie. Poprawne podpisy, które czytnik oznaczy jako nieprawidłowe, są gorsze niż wolniejsza migracja.
  • Zastąp certyfikaty testowe. Pliki PFX w przykładzie są samopodpisane z publicznie dostępnym hasłem, więc wszystko podpisane nimi nie dowodzi niczego.
  • Weryfikuj po podpisaniu w każdym pipeline, używając publicznego certyfikatu, który miałby odbiorca.

Rozwiązywanie typowych problemów

Microsoft Word nie wyświetla podpisu jako ważnego. Oczekiwane na razie: nie ma standardowego identyfikatora XML‑DSig dla ML‑DSA, więc Word może go nie rozpoznać, mimo że podpis jest prawidłowy i GroupDocs.Signature go weryfikuje. Zweryfikuj w własnym pipeline i zachowaj RSA dla dokumentów, których odbiorcy polegają na wskaźniku Worda.

Wywołanie podpisujące odrzuca PDF lub arkusz kalkulacyjny. Podpisy ML‑DSA obejmują formaty Word – DOCX, DOC, ODT i pozostałe. PDF, arkusze i prezentacje nie są jeszcze obsługiwane i nadal należy używać RSA lub ECDSA jak dotąd.

Temat certyfikatu zwraca pusty ciąg. Prawie zawsze pułapka dir() z Kroku 1: atrybut rozwiązuje się dynamicznie, więc odczytaj go zamiast najpierw testować jego istnienie.

Zakończenie

Zmiana w kodzie to zmiana certyfikatu, co jest tym elementem, który sprawia, że warto to zrobić, zanim stanie się pilne. Podpisz długowieczne dokumenty Word przy użyciu ML‑DSA‑65, użyj ML‑DSA‑87 tam, gdzie profil tego wymaga, weryfikuj przy użyciu certyfikatu publicznego i zachowaj RSA tam, gdzie format lub czytnik tego wymagają.

Uruchom przykład na jednej ze swoich umów, a trzy rozmiary pokażą Ci w bajtach, ile kosztuje najsilniejszy dostępny poziom. W pliku, który testowałem, było to 5 769 bajtów.

Dodatkowe zasoby