💡 ตัวอย่างการทำงานเต็มที่มีบน GitHub:
sign-docx-with-mldsa-certificates-python
บทนำ
เซ็นสัญญาในบ่ายวันนี้ด้วย RSA‑2048 แล้วคุณได้ทำสัญญาที่ต้องคงอยู่ตราบใดที่สัญญานั้นมีความสำคัญ หากนั่นคือสองหรือสามทศวรรษ — และสำหรับเอกสาร deeds, consent forms และการอนุมัติวิศวกรรมมักจะเป็นเช่นนั้น — สัญญานั้นต้องยาวเกินกว่าขั้นตอนของอัลกอริทึม การโจมตีไม่จำเป็นต้องมีอยู่ในวันนี้ แต่ต้องมีอยู่ก่อนที่เอกสารจะหยุดมีความสำคัญ แล้วใครก็ตามที่ถือกุญแจสาธารณะก็สามารถสกัดกุญแจส่วนตัวและเซ็นในนามของคุณได้
การเซ็นเอกสารแบบหลังควอนตัมเป็นคุณสมบัติของ GroupDocs.Signature สำหรับ Python ที่แทนที่สัญญานั้นด้วยสัญญาที่สร้างบน ML‑DSA ซึ่งเป็นอัลกอริธึมลายเซ็นที่ NIST ทำมาตรฐานเป็น FIPS 204 ในปี 2024 การสนับสนุนรูปแบบ Word มาถึงใน GroupDocs.Signature 26.9 และใช้ API ที่คุณมีอยู่แล้ว: กุญแจ ML‑DSA อยู่ในไฟล์ PFX และใส่ลงใน DigitalSignOptions เหมือนกับกุญแจ RSA
คู่มือนี้จะเซ็นไฟล์ DOCX ในสี่ขั้นตอน, เปรียบเทียบระดับความปลอดภัยสามระดับบนผลลัพธ์ที่วัดได้, ตรวจสอบลายเซ็นด้วยเพียงใบรับรองสาธารณะ, และสรุปด้วยสองข้อจำกัดที่ควรรู้ก่อนคุณตัดสินใจ
ทำไมเรื่องนี้สำคัญกว่าการย้ายแบบปกติ
การย้ายลายเซ็นแตกต่างจากการย้ายการเข้ารหัสในแง่หนึ่งที่ทำให้สามารถเลื่อนออกไปได้ง่ายและแก้ไขได้ยากกว่า
กับการเข้ารหัส ปัญหา “เก็บข้อมูลตอนนี้แล้วถอดรหัสภายหลัง” เกิดขึ้นทันที: สิ่งที่ดักจับได้วันนี้สามารถเก็บไว้และเปิดอ่านได้ในภายหลัง กับลายเซ็น ไม่มีอะไรที่คุณเซ็นไปแล้วกลายเป็นปลอมได้ย้อนหลัง — แต่ก็ไม่มีอะไรที่คุณเซ็นไว้ยังคงเป็นของคุณอย่างพิสูจน์ได้เช่นกัน เมื่อกุญแจสามารถสกัดจากใบรับรองที่ทุกคนมีสำเนา การเซ็นใหม่ให้กับเอกสารที่เก็บไว้เป็นทศวรรษด้วยกุญแจใหม่เป็นไปได้และไม่มีใครอยากเป็นคนวางแผนเรื่องนี้
เพราะฉะนั้นคำแนะนำเชิงปฏิบัติจึงแคบกว่าแบบกว้าง: ย้ายเอกสารที่ต้องเก็บรักษานาน, ปล่อยส่วนที่เหลือไว้ บางโปรไฟล์ได้ตั้งเกณฑ์ไว้แล้ว — CNSA 2.0 ต้องการ ML‑DSA‑87 สำหรับระบบความมั่นคงระดับชาติ — และสำหรับทุกคนปัจจัยตัดสินคือเอกสารต้องคงความน่าเชื่อถือได้นานเท่าใด
ข้อกำหนดเบื้องต้น
- Python 3.9 หรือใหม่กว่า บนตัวแปล 64‑bit — แพคเกจมาพร้อม .NET runtime ที่บรรจุไว้และไม่มี wheel 32‑bit
- GroupDocs.Signature สำหรับ Python ผ่าน .NET 26.10.0, พร้อม ใบอนุญาตชั่วคราวฟรี เพื่อลบข้อจำกัดการประเมิน
- ใบรับรอง ML‑DSA ที่เป็นไฟล์ PFX ป้องกันด้วยรหัสผ่าน, และเอกสาร Word ที่ต้องการเซ็น
การติดตั้ง
pip install groupdocs-signature-net
ขั้นตอน 1 - เซ็นด้วยใบรับรอง ML‑DSA
ใบรับรองทำหน้าที่ทั้งหมด การเรียกใช้ก็เหมือนที่คุณเขียนสำหรับ RSA:
with signature.Signature(source_path) as sign:
options = DigitalSignOptions(pfx_path)
options.password = CERTIFICATE_PASSWORD
result = sign.sign(output_path, options)
นี่คือเรื่องราวการนำไปใช้ทั้งหมดสำหรับโค้ดที่เคยเซ็นอยู่แล้ว: เพียงชี้ DigitalSignOptions ไปที่ PFX ที่ต่างออกไป ไม่ต้องเพิ่มตัวเลือกใหม่, ไม่ต้องมีพารามิเตอร์อัลกอริธึมแยกต่างหาก, ไม่ต้องมีสาขาสำหรับหลังควอนตัม
การอ่านผู้เซ็นออกมามีอีกขั้นหนึ่งและมีกับดักเฉพาะ Python อยู่ในแบบฝึกหัดนี้:
for created in result.succeeded:
certificate = getattr(created, "certificate", None)
subject = getattr(certificate, "subject", None)
if subject:
return str(subject)
ใบรับรองบน DigitalSignature เป็นอ็อบเจ็กต์สะพานที่แก้ไขแอตทริบิวต์แบบไดนามิก certificate.subject จะคืนค่า CN=GroupDocs.Signature MLDSA65 test ในขณะที่ dir() บนวัตถุเดียวกันนั้นไม่แสดงอะไรเลย ฉันตรวจสอบด้วย dir() ก่อน, สรุปว่า subject ไม่ได้เปิดเผยและจึงสรุปว่าผิด — ดังนั้นหากคุณ introspect ก่อนอ่าน คุณจะพลาดค่าที่มีอยู่
ขั้นตอน 2 - เปรียบเทียบระดับความปลอดภัยสามระดับ
ML‑DSA มีชุดพารามิเตอร์สามชุด และเลือกได้โดยการใช้ใบรับรองที่ต่างกัน:
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)
นี่คือขั้นตอนที่คุ้มค่าที่จะรันจริง เพราะการแลกเปลี่ยนมักอธิบายไว้แต่ไม่ค่อยวัดจริง จากสัญญาต้นฉบับขนาด 132 KB:
| ระดับ | หมวดความปลอดภัยของ NIST | ไฟล์ที่เซ็น | มากกว่าขนาดที่เล็กที่สุด |
|---|---|---|---|
| ML-DSA-44 | 2 | 138,202 ไบต์ | - |
| ML-DSA-65 | 3 | 140,650 ไบต์ | +2,448 ไบต์ |
| ML-DSA-87 | 5 | 143,971 ไบต์ | +5,769 ไบต์ |
ความแตกต่างเพียง 6 KB แยกระหว่างระดับที่อ่อนที่สุดกับที่แข็งแกร่งที่สุด สำหรับสัญญาที่ไม่มีอะไรสำคัญ การตัดสินใจจึงง่าย: ใช้ ML‑DSA‑65 เป็นค่าเริ่มต้น, ML‑DSA‑87 เมื่อโปรไฟล์ต้องการระดับ 5 หรือเมื่อขนาดไม่สำคัญ, และ ML‑DSA‑44 เฉพาะเมื่อคุณต้องเซ็นไฟล์จำนวนมากจนกิโลไบต์รวมกันเป็นสิ่งที่จริงจัง
ขั้นตอน 3 - ตรวจสอบด้วยใบรับรองสาธารณะ
ผู้รับต้องการใบรับรองสาธารณะของผู้เซ็นและไม่มีข้อมูลลับใด ๆ:
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
ตัวอย่างเรียกใช้สองครั้งบนไฟล์เดียวกัน: ครั้งแรกด้วย mldsa65.cer (ครึ่งสาธารณะของกุญแจเซ็น) และครั้งที่สองด้วย PFX ของผู้เซ็นคนอื่น ครั้งแรกคืนค่า True, ครั้งที่สองคืนค่า False โปรดสังเกตว่าใบรับรองที่ผิดจะให้ค่า False แทนการโยนข้อยกเว้น — “เซ็นโดยคนอื่น” เป็นคำตอบที่โค้ดของคุณควรจัดการ, ไม่ใช่ข้อยกเว้น การตรวจสอบครอบคลุมเนื้อหาเอกสารพร้อมกับหมายเลขซีเรียลและ thumbprint ของใบรับรอง ดังนั้นไฟล์ที่แก้ไขหลังการเซ็นก็จะล้มเหลวเช่นกัน
ขั้นตอน 4 - อ่านลายเซ็นจากเอกสาร
เมื่อเอกสารที่เซ็นมาถึงและคุณไม่รู้ว่าจะคาดหวังใบรับรองใด:
with signature.Signature(signed_path) as sign:
found = sign.search(SignatureType.DIGITAL)
for item in found:
print(item.sign_time, item.is_valid)
search กับ SignatureType.DIGITAL จะคืนอ็อบเจ็กต์ DigitalSignature ที่บรรจุใบรับรอง, เวลาเซ็นและแฟล็กความถูกต้อง Word สามารถเก็บลายเซ็นหลายรายการได้, รวมถึงการผสมระหว่าง RSA และ ML‑DSA, และแต่ละรายการจะรายงานด้วยใบรับรองและความถูกต้องของตนเอง
สิ่งนี้ทำให้วิธีการตรวจสอบของผู้รับเปลี่ยนแปลงหรือไม่?
ไม่มีเลยในแง่ที่ผู้รับจะสังเกตเห็น ผู้รับยังคงต้องการเพียงใบรับรองสาธารณะของผู้เซ็น, ยังคงส่งให้กับ DigitalVerifyOptions เดิม, และยังคงรับค่า boolean กลับมา ไม่มีส่วนใดของเส้นทางการตรวจสอบที่เฉพาะเจาะจงกับ ML‑DSA จุดเดียวที่อัลกอริธึมปรากฏคือเครื่องหมายลายเซ็นของ Microsoft Word เอง ซึ่งอาจยังไม่รู้จัก ML‑DSA เนื่องจากรูปแบบไม่มีตัวระบุมาตรฐานสำหรับมัน
การประยุกต์ใช้ในโลกจริง
สัญญาที่ต้องเก็บรักษานาน
กรณีที่ชัดเจนที่สุด เอกสารที่ต้องคงความสามารถตรวจสอบได้หลายทศวรรษเซ็นครั้งเดียวตอนนี้ด้วย ML‑DSA‑65 หรือ ML‑DSA‑87 และไม่ต้องเซ็นใหม่เลยเพราะอัลกอริธึมได้หมดอายุไปแล้ว
สภาพแวดล้อมที่ควบคุมด้วยโปรไฟล์ที่ระบุชื่อ
เมื่อ CNSA 2.0 หรือโปรไฟล์คล้ายกันบังคับใช้ ระดับไม่ใช่การตัดสินใจตามดุลยพินิจ — ML‑DSA‑87 เป็นข้อกำหนด, และคำถามด้านวิศวกรรมที่เหลือคือรูปแบบนั้นรองรับหรือไม่
สายงานผสมระหว่างการย้าย
การเซ็นเอกสารใหม่แบบหลังควอนตัมขณะปล่อยให้คลังเก่าอยู่เป็นสภาพกลางที่สมเหตุสมผล, และการรายงาน search แยกลายเซ็นแต่ละอันทำให้การจัดการเป็นไปได้
แนวทางปฏิบัติที่ดีที่สุดและเคล็ดลับ
- ย้ายตามระยะเวลาการเก็บรักษา ไม่ใช่ตามปริมาณ เอกสารที่ต้องการนี้คือเอกสารที่อายุยาว; ใบเสร็จที่สำคัญแค่ 90 วันไม่ต้องการ
- ตั้งค่าเริ่มต้นเป็น ML‑DSA‑65 เว้นแต่โปรไฟล์จะระบุระดับอื่น, และไม่ต้องกังวลเรื่องขนาด — แตกต่างไม่ถึง 6 KB ต่อลายเซ็น
- คง RSA ไว้เมื่อผู้รับตรวจสอบใน Word ลายเซ็นที่ถูกทำเครื่องหมายว่าไม่ถูกต้องโดยโปรแกรมอ่านนั้นแย่กว่าการย้ายที่ช้ากว่า
- เปลี่ยนใบรับรองทดสอบ ไฟล์ PFX ของตัวอย่างเป็น self‑signed พร้อมรหัสผ่านที่เผยแพร่, ดังนั้นสิ่งที่เซ็นด้วยมันไม่ให้ความเชื่อถือใด ๆ
- ตรวจสอบหลังการเซ็น ในทุกสายงาน, ใช้ใบรับรองสาธารณะที่ผู้รับจะมี
การแก้ไขปัญหาที่พบบ่อย
Microsoft Word ไม่แสดงลายเซ็นว่าเป็นที่ถูกต้อง คาดหวังได้ในตอนนี้: ยังไม่มีตัวระบุ XML‑DSig มาตรฐานสำหรับ ML‑DSA, ดังนั้น Word อาจไม่รู้จักแม้ว่าลายเซ็นจะถูกต้องและ GroupDocs.Signature ตรวจสอบผ่านแล้ว ตรวจสอบในสายงานของคุณเองและคง RSA ไว้สำหรับเอกสารที่ผู้รับพึ่งพาเครื่องหมายของ Word
คำสั่งเซ็นปฏิเสธไฟล์ PDF หรือสเปรดชีต การเซ็น ML‑DSA ครอบคลุมรูปแบบ Word — DOCX, DOC, ODT และอื่น ๆ PDF, สเปรดชีตและพรีเซนเทชันยังไม่รองรับ, จึงต้องเซ็นด้วย RSA หรือ ECDSA เหมือนเดิม
หัวข้อของใบรับรองกลับเป็นค่าว่าง เกือบทั้งหมดเป็นกับดัก dir() จากขั้นตอน 1: แอตทริบิวต์ถูกแก้ไขแบบไดนามิก, ดังนั้นให้อ่านค่าแทนการตรวจสอบว่ามีหรือไม่ก่อน
สรุป
การเปลี่ยนแปลงโค้ดคือการเปลี่ยนใบรับรอง, ซึ่งเป็นส่วนที่ทำให้การทำเช่นนี้คุ้มค่าก่อนที่มันจะเร่งด่วน เซ็นเอกสาร Word ที่อายุยาวด้วย ML‑DSA‑65, ใช้ ML‑DSA‑87 เมื่อโปรไฟล์กำหนด, ตรวจสอบด้วยใบรับรองสาธารณะ, และคง RSA ไว้เมื่อรูปแบบหรือโปรแกรมอ่านต้องการ
รันตัวอย่างกับสัญญาใดสัญญาหนึ่งของคุณเองและสามขนาดจะบอกคุณเป็นไบต์ว่าระดับที่แข็งแกร่งที่สุดมีค่าใช้จ่ายเท่าไหร่ ในไฟล์ที่ฉันทดสอบค่าเพิ่มคือ 5,769 ไบต์