💡 Full working example available on GitHub:
sign-docx-with-mldsa-certificates-python

Giới thiệu

Ký một hợp đồng vào buổi chiều nay bằng RSA-2048 và bạn đã đưa ra một lời hứa phải giữ vững trong suốt thời gian hợp đồng còn có giá trị. Nếu thời gian đó là hai, ba mươi năm — và đối với các giấy tờ, mẫu đồng ý và phê duyệt kỹ thuật thì thường là như vậy — lời hứa phải tồn tại lâu hơn thuật toán. Cuộc tấn công không cần phải tồn tại ngay hôm nay. Nó chỉ cần tồn tại trước khi tài liệu không còn quan trọng, và sau đó bất kỳ ai có khóa công khai cũng có thể suy ra khóa riêng và ký thay bạn.

Chữ ký tài liệu hậu lượng tử là tính năng của GroupDocs.Signature cho Python, thay thế lời hứa đó bằng một lời hứa dựa trên ML-DSA, thuật toán ký được NIST chuẩn hoá thành FIPS 204 vào năm 2024. Hỗ trợ định dạng Word đã xuất hiện trong GroupDocs.Signature 26.9, và nó tái sử dụng API bạn đã có: một khóa ML-DSA nằm trong PFX và được đưa vào DigitalSignOptions giống như khóa RSA.

Hướng dẫn này ký một tệp DOCX trong bốn bước, so sánh ba mức bảo mật dựa trên đầu ra đo được, xác thực chữ ký chỉ bằng một chứng chỉ công khai, và kết thúc với hai giới hạn cần biết trước khi bạn cam kết.

Tại sao điều này quan trọng hơn so với việc di chuyển thông thường

Di chuyển chữ ký khác với di chuyển mã hoá ở một khía cạnh khiến việc hoãn lại dễ dàng hơn và việc sửa chữa trở nên khó xử hơn.

Với mã hoá, vấn đề “thu thập‑bây‑giờ‑giải‑mã‑sau‑này” là ngay lập tức: bất cứ thứ gì bị chặn hôm nay có thể được lưu trữ và mở ra sau. Với chữ ký, không có gì bạn đã ký trước đây trở nên có thể giả mạo một cách hồi tố — nhưng cũng không có gì bạn đã ký vẫn chứng minh được là của bạn, một khi khóa có thể được suy ra từ chứng chỉ mà mọi người đều có bản sao. Việc ký lại một thập kỷ tài liệu lưu trữ bằng khóa mới là khả thi và không ai muốn là người lên kế hoạch cho việc đó.

Vì vậy lời khuyên thực tế là hẹp hơn là rộng rãi: di chuyển những tài liệu có thời gian lưu trữ dài, để lại phần còn lại. Một số tiêu chuẩn đã đặt ra mức chuẩn — CNSA 2.0 yêu cầu ML-DSA-87 cho các hệ thống an ninh quốc gia — và đối với những người khác, yếu tố quyết định là thời gian tài liệu phải còn khả năng bảo vệ.

Điều kiện tiên quyết

  • Python 3.9 trở lên trên môi trường 64‑bit — gói này đi kèm một runtime .NET được đóng gói và không có bản wheel 32‑bit
  • GroupDocs.Signature cho Python qua .NET 26.10.0, với một giấy phép tạm thời miễn phí để loại bỏ giới hạn đánh giá
  • Một chứng chỉ ML-DSA dưới dạng PFX có bảo vệ bằng mật khẩu, và một tài liệu Word để ký

Cài đặt

pip install groupdocs-signature-net

Bước 1 – Ký bằng chứng chỉ ML-DSA

Chứng chỉ thực hiện công việc. Lệnh gọi giống như khi bạn ký bằng RSA:

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

    result = sign.sign(output_path, options)

Đó là toàn bộ câu chuyện áp dụng cho mã đã ký: chỉ cần trỏ DigitalSignOptions tới một PFX khác. Không có tùy chọn mới, không có tham số thuật toán riêng, không có nhánh cho hậu lượng tử.

Đọc lại người ký cần một bước nữa, và chứa bẫy duy nhất liên quan đến Python trong toàn bộ bài tập này:

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

Chứng chỉ trên một DigitalSignature là một đối tượng cầu nối giải quyết các thuộc tính một cách động. certificate.subject trả về CN=GroupDocs.Signature MLDSA65 test, trong khi dir() trên cùng đối tượng đó không liệt kê gì cả. Tôi đã kiểm tra bằng dir() trước, kết luận rằng thuộc tính subject không được phơi bày, và đã sai — vì vậy nếu bạn introspect trước khi đọc, bạn sẽ bỏ qua một giá trị đang tồn tại.

Bước 2 – So sánh ba mức bảo mật

ML-DSA có ba bộ tham số, và chúng được chọn bằng cách cung cấp một chứng chỉ khác nhau:

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)

Đây là bước đáng để thực hiện, vì sự đánh đổi thường chỉ được mô tả mà hiếm khi đo lường. Từ một hợp đồng nguồn 132 KB:

Mức Hạng mục bảo mật NIST Tệp đã ký So với tệp nhỏ nhất
ML-DSA-44 2 138 202 byte -
ML-DSA-65 3 140 650 byte +2 448 byte
ML-DSA-87 5 143 971 byte +5 769 byte

Chỉ dưới 6 KB tách biệt mức yếu nhất khỏi mức mạnh nhất. Đối với một hợp đồng, con số này không đáng kể, nên quyết định trở nên đơn giản: dùng ML-DSA-65 làm mặc định, ML-DSA-87 khi một hồ sơ yêu cầu hạng mục 5 hoặc khi kích thước không quan trọng, và ML-DSA-44 chỉ khi bạn ký quá nhiều tệp khiến kilobyte tích lũy thành một khối lượng đáng kể.

Bước 3 – Xác thực bằng chứng chỉ công khai

Người nhận chỉ cần chứng chỉ công khai của người ký và không cần gì bí mật:

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

Mẫu gọi hàm này hai lần trên cùng một tệp: một lần với mldsa65.cer, nửa công khai của khóa ký, và một lần với một PFX của người ký khác. Lần đầu trả về True, lần thứ hai trả về False. Lưu ý rằng chứng chỉ sai trả về False thay vì ném ngoại lệ — “được ký bởi người khác” là câu trả lời mà mã của bạn nên xử lý, không phải là một lỗi. Kiểm tra bao gồm nội dung tài liệu cùng với số sê-ri và dấu vân tay của chứng chỉ, vì vậy một tệp bị chỉnh sửa sau khi ký cũng sẽ thất bại.

Bước 4 – Đọc các chữ ký trong tài liệu

Khi một tài liệu đã ký đến và bạn không biết sẽ nhận được chứng chỉ nào:

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

search với SignatureType.DIGITAL trả về các đối tượng DigitalSignature mang theo chứng chỉ, thời gian ký và cờ hợp lệ. Một tài liệu Word có thể chứa nhiều chữ ký, bao gồm cả sự kết hợp giữa RSA và ML-DSA, và mỗi chữ ký sẽ được báo cáo với chứng chỉ và trạng thái hợp lệ riêng của nó.

Điều này có thay đổi cách người nhận xác thực không?

Không hề, ít nhất là họ sẽ không nhận thấy. Người nhận vẫn chỉ cần chứng chỉ công khai của người ký, vẫn truyền nó vào cùng một DigitalVerifyOptions, và vẫn nhận về một giá trị boolean. Không có gì trong quy trình xác thực đặc thù cho ML-DSA. Điểm duy nhất thuật toán hiện ra là chỉ báo chữ ký của Microsoft Word, có thể chưa nhận ra ML-DSA vì định dạng chưa có định danh chuẩn cho nó.

Ứng dụng thực tế

Hợp đồng lưu trữ lâu dài

Trường hợp rõ ràng nhất. Một tài liệu phải duy trì khả năng xác thực trong nhiều thập kỷ được ký một lần, ngay bây giờ, bằng ML-DSA-65 hoặc ML-DSA-87, và không cần ký lại vì thuật toán đã lỗi thời.

Môi trường được quy định với hồ sơ đã định danh

Khi CNSA 2.0 hoặc một hồ sơ tương tự áp dụng, mức độ không phải là quyết định tùy ý — ML-DSA-87 là yêu cầu, và câu hỏi kỹ thuật duy nhất là định dạng có được hỗ trợ hay không.

Đường ống hỗn hợp trong quá trình di chuyển

Ký các tài liệu mới bằng hậu lượng tử trong khi để nguyên kho lưu trữ là một trạng thái trung gian hoàn toàn hợp lý, và việc search báo cáo từng chữ ký riêng biệt là điều giúp quản lý được.

Thực hành tốt và mẹo

  • Di chuyển dựa trên thời gian lưu trữ, không phải khối lượng. Những tài liệu cần điều này là những tài liệu tồn tại lâu dài; một biên nhận chỉ quan trọng trong 90 ngày thì không cần.
  • Mặc định sử dụng ML-DSA-65 trừ khi hồ sơ quy định mức khác, và đừng lo lắng quá về chênh lệch kích thước — dưới 6 KB cho mỗi chữ ký.
  • Giữ RSA khi người nhận xác thực trong Word. Các chữ ký đúng mà trình đọc đánh dấu là tệ hơn so với một quá trình di chuyển chậm hơn.
  • Thay thế các chứng chỉ thử nghiệm. Các tệp PFX mẫu được tự ký với mật khẩu công khai, vì vậy bất kỳ tài liệu nào ký bằng chúng cũng không chứng minh gì.
  • Xác thực sau khi ký trong bất kỳ đường ống nào, sử dụng chứng chỉ công khai mà người nhận sẽ có.

Khắc phục các vấn đề thường gặp

Microsoft Word không hiển thị chữ ký là hợp lệ. Hiện tại đây là điều bình thường: không có định danh XML‑DSig chuẩn cho ML-DSA, vì vậy Word có thể không nhận ra dù chữ ký đúng và GroupDocs.Signature xác thực được. Hãy xác thực trong quy trình của bạn và giữ RSA cho các tài liệu mà người nhận dựa vào chỉ báo của Word.

Lệnh ký bị từ chối với PDF hoặc bảng tính. Chữ ký ML-DSA hiện chỉ hỗ trợ các định dạng Word — DOCX, DOC, ODT và các định dạng tương tự. PDF, bảng tính và bản trình chiếu chưa được hỗ trợ, và vẫn phải ký bằng RSA hoặc ECDSA như trước.

Thuộc tính subject của chứng chỉ trả về rỗng. Hầu hết là do bẫy dir() ở Bước 1: thuộc tính được giải quyết động, vì vậy hãy đọc nó thay vì kiểm tra sự tồn tại trước.

Kết luận

Thay đổi mã chỉ là thay đổi chứng chỉ, và đó là phần khiến việc thực hiện trở nên đáng giá trước khi trở nên cấp bách. Hãy ký các tài liệu Word lâu dài bằng ML-DSA-65, dùng ML-DSA-87 khi hồ sơ yêu cầu, xác thực bằng chứng chỉ công khai, và giữ RSA khi định dạng hoặc trình đọc yêu cầu.

Chạy mẫu trên một trong những hợp đồng của bạn và ba kích thước sẽ cho bạn biết, tính bằng byte, chi phí thực sự của mức bảo mật mạnh nhất. Trên tệp tôi thử nghiệm, chi phí là 5 769 byte.

Tài nguyên bổ sung