ข้ามไปยังเนื้อหา

ลายเซ็นอยู่ใน PDF อย่างไร

Spec: ETSI EN 319 142-1 Spec: RFC 5652 Evidence: Standard-backed

ลายเซ็น PDF ไม่ได้ห่อหุ้มไฟล์จากภายนอก แต่ฝังอยู่ ภายใน ไฟล์ในรูปพจนานุกรมที่ระบุชื่อลายเซ็น และไดเจสต์ที่คำนวณจากช่วงไบต์ที่ประกาศไว้ โดยจงใจข้ามค่าลายเซ็นเอง หน้านี้อธิบายกลไกดังกล่าว และที่สำคัญไม่แพ้กันคือสิ่งที่กลไกนี้ไม่ได้รับประกัน

“เอกสารนี้ลงนามแล้ว” เป็นประโยคที่ผู้คนใช้ประกอบการตัดสินใจ ประโยคนี้เชื่อมโยงกับการชำระเงิน การอนุมัติ หรือภาระผูกพันทางกฎหมาย หากไม่ทราบอย่างแม่นยำว่าลายเซ็นครอบคลุมไบต์ใดบ้าง ก็ไม่สามารถบอกได้ว่าผลลัพธ์ที่ถูกต้องนั้นพิสูจน์อะไรได้จริง PDF อาจมีลายเซ็นที่ถูกต้องครบถ้วน แต่ยังคงแสดงเนื้อหาต่อผู้อ่านที่ผู้ลงนามไม่เคยเห็น เพราะเนื้อหานั้นถูกเพิ่มเข้ามา หลัง การลงนาม ในส่วนที่ลายเซ็นไม่เคยอ้างสิทธิ์ไว้ การรู้ว่าอำนาจของลายเซ็นเริ่มต้นและสิ้นสุดที่ใด คือความแตกต่างระหว่างการตัดสินใจที่ปกป้องได้กับการตัดสินใจที่อาศัยความหวัง

  • ลายเซ็น PDF อยู่ใน พจนานุกรมลายเซ็น และ ฟิลด์ลายเซ็น ภายในเอกสาร ไม่ใช่อยู่ในซองหุ้มภายนอก
  • ไบต์ที่ลงนามถูกประกาศโดยอาร์เรย์ ByteRange ซึ่งประกอบด้วยเซกเมนต์ (offset, length) สองเซกเมนต์ที่รวมกันครอบคลุมทั้งไฟล์ ยกเว้น ค่าลายเซ็นรูปเลขฐานสิบหกที่เก็บอยู่ในรายการ Contents
  • ไดเจสต์ของสองเซกเมนต์ที่นำมาต่อกันคือสิ่งที่ลายเซ็นเชิงวิทยาการเข้ารหัสลับปกป้องจริง
  • สิ่งใดก็ตามที่ถูกผนวกเข้ามาภายหลังในการแก้ไขใหม่จะอยู่ นอก byte range เดิม ลายเซ็นเดิมยังคงถูกต้อง เพราะไม่เคยอ้างสิทธิ์ใด ๆ ต่อไบต์ใหม่
  • ลายเซ็นแบบ อนุมัติ กับลายเซ็นแบบ รับรอง มีขอบเขตต่างกัน การรับรอง (DocMDP) จำกัดว่าการเปลี่ยนแปลงใดในภายหลังที่อนุญาตได้ ส่วนการอนุมัติไม่จำกัดการเปลี่ยนแปลงเหล่านั้น

NextPDF สร้างลายเซ็นตามแบบจำลองที่รูปแบบไฟล์กำหนด ตามลำดับที่ตายตัว เพื่อให้ byte range มีความแม่นยำแทนที่จะเป็นค่าประมาณ

เมื่อเอนจินเขียนลายเซ็น จะจองช่องขนาดคงที่สำหรับ ค่า Contents ไว้ก่อน และเขียนตัวยึดตำแหน่ง ByteRange ที่มีความกว้างคงที่ เอนจิน จะรอจนกว่าเอกสารทั้งฉบับจะถูกเขียนเสร็จสมบูรณ์ รวมถึงตาราง cross-reference และเครื่องหมายสิ้นสุดไฟล์ จากนั้นจึงคำนวณออฟเซ็ตจริงทั้งสองค่า เขียนค่ากลับลงในตัวยึดตำแหน่งโดยไม่ทำให้ไบต์ใด ๆ เลื่อน แฮชเซกเมนต์ทั้งสอง และวางออบเจ็กต์ CMS ที่ได้ลงในช่องที่จองไว้ ตัวยึดตำแหน่ง ถูกเติมศูนย์ให้มีความยาวคงที่โดยเจาะจง เพื่อให้การเติม ตัวเลขจริงลงไปไม่สามารถขยับไบต์ที่กำลังถูกแฮชได้ นี่คือลำดับเดียวที่ ทำให้ลายเซ็นสอดคล้องกับตัวเอง เอนจินถือว่าความล้มเหลวใด ๆ ในลำดับนี้ เป็นข้อผิดพลาดร้ายแรง ไม่ใช่ทางสำรองแบบเงียบ ๆ

สำหรับโปรไฟล์ PDF 2.0 ออบเจ็กต์ลายเซ็นตัวมันเองเป็นโครงสร้าง CMS SignedData แบบแยกส่วน พจนานุกรม PDF บอก ที่ไหน และ อย่างไร ส่วนออบเจ็กต์ CMS ถือข้อมูลว่า ใคร ลงนาม และหลักฐานเชิงวิทยาการเข้ารหัสลับ

  1. Step 1 of 4: ISO 32000-2 §12.8.1 — ByteRange digest & signature dictionary
  2. Step 2 of 4: ISO 32000-2 §12.8.3.3 — ETSI.CAdES.detached SubFilter
  3. Step 3 of 4: ETSI EN 319 142-1 PAdES baseline profile
  4. Step 4 of 4: RFC 5652 CMS SignedData in Contents
ที่ที่ลายเซ็น PDF ถูกนิยาม ตั้งแต่รูปแบบคอนเทนเนอร์ไปจนถึงออบเจ็กต์เชิงวิทยาการเข้ารหัสลับ ISO 32000-2 ระบุพจนานุกรมและกลไก byte-range ETSI EN 319 142-1 ทำโปรไฟล์ให้แก่ PAdES และ RFC 5652 นิยามออบเจ็กต์ CMS SignedData ที่วางอยู่ใน Contents

Evidence: Standard-backed กลไกนี้ถูกนิยามโดย Spec: ISO 32000-2, §12.8.1 ไดเจสต์แบบ byte-range ถูกคำนวณจากช่วงไบต์ที่ระบุโดยรายการ ByteRange ช่วงดังกล่าว ควรเป็นทั้งไฟล์ รวมถึง พจนานุกรมลายเซ็น แต่ ยกเว้น ค่าลายเซ็นซึ่งอยู่ในรายการ Contents ส่วน ByteRange เป็นอาร์เรย์ของ คู่จำนวนเต็ม — ออฟเซ็ตเริ่มต้นและความยาว ช่วงที่ไม่ต่อเนื่อง มีไว้โดยเฉพาะเพื่อให้ไดเจสต์สามารถละค่าลายเซ็นตัวมันเองได้

สำหรับโปรไฟล์ PDF 2.0 Spec: ISO 32000-2, §12.8.3.3 ระบุว่าเมื่อ SubFilter เป็น ETSI.CAdES.detached ค่า Contents จะเป็นออบเจ็กต์ CMS SignedData ที่เข้ารหัสแบบ DER ซึ่งเป็นโครงสร้างเดียวกับที่ Spec: RFC 5652 นิยาม — และโปรไฟล์ PAdES ของออบเจ็กต์นั้น คือสิ่งที่ Spec: ETSI EN 319 142-1 อธิบายไว้

ขอบเขตของลายเซ็นแต่ละประเภทไม่เหมือนกัน Spec: ISO 32000-2, §12.7.4.5 นิยามสิทธิ์ MDP โดยค่า 0 ทำให้ลายเซ็นเป็นลายเซ็นแบบ อนุมัติ ขณะที่ค่า 13 ทำให้เป็นลายเซ็นแบบ รับรอง ที่จำกัดว่าการแก้ไขใดในภายหลังที่ยังคงทำให้เอกสารเป็นไปตามข้อกำหนด กลไก byte-range เหมือนกัน แต่ให้คำมั่นเกี่ยวกับอนาคตต่างกัน

เอนจินของ NextPDF ทำตามลำดับนี้อย่างแม่นยำ ได้แก่ ตัวยึดตำแหน่ง ByteRange ความกว้างคงที่ ไดเจสต์จากสองเซกเมนต์ที่ต่อกัน และออบเจ็กต์ CMS แบบแยกส่วนในช่อง Contents ที่จองไว้ ซึ่งจะถูกสรุปขั้นสุดท้ายเมื่อไฟล์เสร็จสมบูรณ์แล้วเท่านั้น

โดยทั่วไปแทบไม่จำเป็นต้องสร้าง ByteRange ด้วยมือ จุดประสงค์ของตัวอย่างนี้คือการแสดง รูปร่าง ของผลลัพธ์ เพื่อให้จำได้เมื่อคุณตรวจสอบไฟล์ที่ลงนามแล้ว

<?php
declare(strict_types=1);
use NextPDF\Security\Signature\ByteRangeCalculator;
// Offsets the engine knows only after the whole PDF is written:
// $contentsStart — byte just before the '<' of the hex signature
// $contentsEnd — byte just after the '>' that closes it
// $fileLength — total file size in bytes
$range = ByteRangeCalculator::calculate(
contentsStart: $contentsStart,
contentsEnd: $contentsEnd,
fileLength: $fileLength,
);
// $range === [0, $contentsStart, $contentsEnd, $fileLength - $contentsEnd]
// Segment 1: file start → just before the signature value
// Segment 2: just after the signature value → end of file
// The signature value itself is the gap. It is never hashed.
$signedMessage = ByteRangeCalculator::extractSignedData($pdfBytes, $range);
// $signedMessage is segment 1 concatenated with segment 2 — exactly the
// bytes the cryptographic digest is computed over.

ช่องว่างระหว่างสองเซกเมนต์คือค่าลายเซ็น ค่าลายเซ็นไม่สามารถเป็นส่วนหนึ่งของไดเจสต์ของตัวมันเองได้ จึงเป็นเหตุให้ช่วงนี้ถูกแบ่งเป็นสองส่วน ไม่ใช่ส่วนเดียว

ข้อผิดพลาดที่พบบ่อยคือการเชื่อว่าลายเซ็นที่ถูกต้องหมายความว่า ทั้งไฟล์ที่คุณกำลังดูอยู่ คือสิ่งที่ถูกลงนาม ความหมายจริงไม่ใช่เช่นนั้น แต่คือไบต์ภายในช่วงที่ประกาศไว้ยังคงสมบูรณ์ การแก้ไขในภายหลังสามารถผนวกเนื้อหาอย่างถูกต้องตามหลักได้ — ลายเซ็นที่สอง ข้อมูลฟอร์ม วัสดุการตรวจสอบ — โดยอยู่ นอก ช่วงนั้น ลายเซ็นแรกยังคงถูกต้อง และไม่ได้บอกอะไรเกี่ยวกับส่วนที่เพิ่มเข้ามา โปรแกรมดูที่ถูกต้องจะบอกคุณว่าลายเซ็นครอบคลุม “เอกสารตามที่มีอยู่ ณ เวลาที่ลงนาม” ไม่ใช่ “ทุกไบต์ที่ปรากฏบนหน้าจอ” การถือว่าสองสิ่งนี้เหมือนกันคือเหตุผลที่เอกสารที่ลงนามแล้วอาจมีเนื้อหาที่ไม่ได้ลงนาม แต่ดูเหมือนถูกลงนาม

หน้านี้อธิบาย โครงสร้าง ไม่ใช่ ความน่าเชื่อถือ ออบเจ็กต์ ByteRange และ CMS ที่ประกอบขึ้นอย่างถูกต้องจะบอกคุณว่าไบต์ยังคงสมบูรณ์และคีย์ใดลงนาม บนเอกสาร แต่สิ่งเหล่านี้เพียงอย่างเดียวไม่ได้บอกว่าคีย์นั้นเป็นของผู้ที่คุณ คิดหรือไม่ ใบรับรองของคีย์นั้นถูกต้อง ณ เวลาที่ลงนามหรือไม่ หรือถูกเพิกถอน ในภายหลังหรือไม่ นั่นเป็นงานของ certificate-path และการเพิกถอน ซึ่งครอบคลุมอยู่ใน การตรวจสอบความถูกต้องของลายเซ็นอย่างเหมาะสม หน้านี้ยังไม่ครอบคลุมการพิสูจน์ด้วยอำนาจอิสระว่าการลงนามเกิดขึ้น เมื่อใด เวลาลงนามที่ระบุด้วยตนเองไม่ใช่เวลาที่น่าเชื่อถือ — ดู ไทม์สแตมป์และเวลาที่น่าเชื่อถือ NextPDF สร้างโครงสร้างที่อธิบายไว้ที่นี่ ส่วนใบรับรอง trust anchor และผู้ออกไทม์สแตมป์นั้นมาจากการปรับใช้ของคุณ ไม่ใช่โดยเอนจิน

ความสามารถที่เอนจินมอบให้ในแต่ละระดับชั้นคือการสร้างโครงสร้างดังนี้

โครงสร้างลายเซ็น PAdES (byte range, พจนานุกรมลายเซ็น, CMS แบบแยกส่วน) — edition availability
Edition Availability
Core

PAdES B-B: พจนานุกรมลายเซ็น ByteRange ความกว้างคงที่ และ ออบเจ็กต์ CMS SignedData แบบแยกส่วนตามที่อธิบายไว้ในหน้านี้

Pro

เพิ่ม PAdES B-T — ไทม์สแตมป์ที่น่าเชื่อถือบนค่าลายเซ็น — บน โครงสร้างเดียวกัน

Enterprise

เพิ่มโปรไฟล์ระยะยาว (B-LT, B-LTA): วัสดุการตรวจสอบที่ฝังไว้ และไทม์สแตมป์ของเอกสารที่ซ้อนบนรากฐาน byte-range เดียวกัน

  • พจนานุกรมลายเซ็น — พจนานุกรม PDF ที่ระบุชื่อตัวจัดการลายเซ็น SubFilter ByteRange และค่า Contents
  • ByteRange — อาร์เรย์ของคู่จำนวนเต็ม (offset, length) ที่ประกาศไบต์ที่แน่นอนซึ่งไดเจสต์ของลายเซ็นครอบคลุม
  • Contents — รายการเลขฐานสิบหกที่เก็บค่าลายเซ็น (สำหรับ PDF 2.0 เป็นออบเจ็กต์ CMS SignedData แบบแยกส่วน) และถูกแยกออกจากไดเจสต์ของตัวมันเอง
  • CMS SignedData — โครงสร้าง Cryptographic Message Syntax (RFC 5652) ที่เก็บใบรับรองของผู้ลงนามและไบต์ของลายเซ็น
  • PAdES — PDF Advanced Electronic Signatures: โปรไฟล์ของ ETSI สำหรับลายเซ็น CMS ของ PDF ตามที่นิยามไว้ในชุด ETSI EN 319 142
  • ลายเซ็นแบบอนุมัติ — ลายเซ็นที่มีสิทธิ์ MDP เป็น 0 ลายเซ็นนี้ยืนยันเนื้อหาโดยไม่จำกัดการเปลี่ยนแปลงในภายหลัง
  • ลายเซ็นแบบรับรอง — ลายเซ็นที่มีสิทธิ์ DocMDP (MDP 13) ที่จำกัดว่าการแก้ไขใดในภายหลังยังคงทำให้เอกสารเป็นไปตามข้อกำหนด