Bỏ qua để đến nội dung

Chữ ký nằm ở đâu trong tệp PDF

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

Một chữ ký PDF không bọc bên ngoài tệp. Nó được nhúng bên trong tệp: một từ điển định danh chữ ký và một digest được tính trên dải byte đã khai báo, chủ ý bỏ qua chính giá trị chữ ký. Trang này giải thích cơ chế đó và, cũng quan trọng không kém, những điều nó không cam kết.

Câu “Tài liệu đã được ký” thường là cơ sở để mọi người ra quyết định. Họ gắn câu đó với một khoản thanh toán, một sự phê duyệt, một nghĩa vụ pháp lý. Nếu bạn không biết chính xác chữ ký bao phủ những byte nào, bạn không thể nói một kết quả hợp lệ thực sự chứng minh điều gì. Một tệp PDF có thể mang một chữ ký hoàn toàn hợp lệ mà vẫn hiển thị cho người đọc nội dung người ký chưa từng thấy, vì nội dung đó được thêm vào sau khi ký, trong một vùng mà chữ ký chưa bao giờ tuyên bố bao phủ. Biết rõ thẩm quyền của chữ ký bắt đầu và kết thúc ở đâu là khác biệt giữa một quyết định có cơ sở vững chắc và một quyết định chỉ dựa trên hy vọng.

  • Một chữ ký PDF nằm trong một từ điển chữ ký và một trường chữ ký bên trong tài liệu, chứ không phải là lớp bọc bên ngoài.
  • Các byte được ký được khai báo bằng một mảng ByteRange: hai phân đoạn (offset, length) cùng nhau bao phủ toàn bộ tệp ngoại trừ giá trị chữ ký dạng thập lục phân được giữ trong mục Contents.
  • Digest của hai phân đoạn được nối lại đó là thứ mà chữ ký mật mã thực sự bảo vệ.
  • Mọi thứ được thêm vào sau đó trong một bản sửa đổi mới đều nằm ngoài byte range ban đầu. Chữ ký ban đầu vẫn hợp lệ; nó chưa từng tuyên bố gì về các byte mới.
  • Một chữ ký phê duyệt và một chữ ký chứng nhận khác nhau ở phạm vi: chứng nhận (DocMDP) ràng buộc những thay đổi nào được phép sau này; phê duyệt thì không.

NextPDF dựng chữ ký theo đúng mô hình của định dạng, theo một thứ tự cố định, để byte range chính xác chứ không chỉ gần đúng.

Khi engine ghi một chữ ký, trước tiên nó dành sẵn một vùng có kích thước cố định cho giá trị Contents rồi ghi một chỗ giữ chỗ ByteRange có độ rộng cố định. Nó chờ cho đến khi toàn bộ tài liệu được ghi xong, bao gồm cả bảng tham chiếu chéo và dấu kết thúc tệp. Chỉ khi đó nó mới tính hai offset thực, ghi chúng trở lại chỗ giữ chỗ mà không làm dịch chuyển bất kỳ byte nào, băm hai phân đoạn, rồi đặt đối tượng CMS thu được vào vùng đã dành sẵn. Chỗ giữ chỗ được đệm bằng số 0 đến một độ dài cố định, chính là để việc điền vào các con số thực không thể làm dịch chuyển các byte đang được băm. Đây là thứ tự duy nhất tạo ra một chữ ký nhất quán với chính nó. Engine xử lý mọi thất bại trong trình tự này như một lỗi nghiêm trọng thay vì âm thầm dùng phương án dự phòng.

Với hồ sơ PDF 2.0, bản thân đối tượng chữ ký là một cấu trúc CMS tách rời SignedData. Từ điển PDF cho biết ở đâunhư thế nào; đối tượng CMS mang theo ai và bằng chứng mật mã.

  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
Nơi một chữ ký PDF được định nghĩa, từ định dạng vùng chứa đi xuống đối tượng mật mã: ISO 32000-2 quy định từ điển và cơ chế byte-range, ETSI EN 319 142-1 tạo hồ sơ cho nó theo PAdES, và RFC 5652 định nghĩa đối tượng CMS SignedData được đặt trong Contents.

Evidence: Standard-backed Cơ chế này được định nghĩa bởi Spec: ISO 32000-2, §12.8.1 . Một digest byte-range được tính trên dải byte do mục ByteRange chỉ định. Dải đó phải là toàn bộ tệp bao gồm từ điển chữ ký nhưng loại trừ giá trị chữ ký — tức mục Contents. ByteRange là một mảng các cặp số nguyên — offset bắt đầu và độ dài. Các dải không liền nhau được dùng có chủ đích để digest có thể bỏ qua chính giá trị chữ ký.

Với hồ sơ PDF 2.0, Spec: ISO 32000-2, §12.8.3.3 quy định rằng khi SubFilterETSI.CAdES.detached, giá trị Contents là một đối tượng CMS SignedData được mã hóa DER — cùng cấu trúc Spec: RFC 5652 định nghĩa — và hồ sơ PAdES của đối tượng đó chính là hồ sơ mà Spec: ETSI EN 319 142-1 mô tả.

Phạm vi giữa các chữ ký không giống nhau. Spec: ISO 32000-2, §12.7.4.5 định nghĩa quyền MDP: giá trị 0 biến chữ ký thành chữ ký phê duyệt, trong khi các giá trị 13 biến nó thành chữ ký chứng nhận ràng buộc những sửa đổi nào về sau vẫn giữ tài liệu ở trạng thái tuân thủ. Cơ chế byte-range vẫn là một; cam kết cho tương lai thì khác.

Engine của NextPDF hiện thực hóa đúng mô hình này: một chỗ giữ chỗ ByteRange có độ rộng cố định, digest nối hai phân đoạn, và một đối tượng CMS tách rời trong một vùng Contents đã dành sẵn, chỉ được hoàn tất sau khi tệp hoàn chỉnh.

Bạn hiếm khi phải tự tay dựng một ByteRange. Mục đích của ví dụ là cho thấy hình dạng của kết quả để bạn nhận ra nó khi kiểm tra một tệp đã được ký.

<?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.

Khoảng trống giữa hai phân đoạn chính là giá trị chữ ký. Nó không thể là một phần của digest của chính nó; đó là lý do dải gồm hai phần, chứ không phải một.

Cái bẫy nằm ở niềm tin rằng một chữ ký hợp lệ có nghĩa là toàn bộ tệp mà bạn đang xem chính là thứ đã được ký. Không phải vậy. Nó có nghĩa là các byte nằm trong dải đã khai báo còn nguyên vẹn. Một bản sửa đổi về sau có thể thêm nội dung một cách hợp lệ — một chữ ký thứ hai, dữ liệu biểu mẫu, vật liệu xác thực — ngoài dải đó. Chữ ký đầu tiên vẫn hợp lệ, và nó không nói gì về phần được thêm vào. Một trình xem đúng đắn sẽ cho bạn biết một chữ ký bao phủ “tài liệu ở trạng thái tồn tại tại thời điểm ký”, chứ không phải “mọi byte trên màn hình.” Đánh đồng hai điều này là cách một tài liệu đã ký có thể nhận thêm nội dung chưa ký nhưng vẫn trông như đã ký.

Trang này giải thích cấu trúc, chứ không phải sự tin cậy. Một ByteRange được tạo đúng cùng đối tượng CMS cho bạn biết các byte còn nguyên vẹn và khóa nào đã ký chúng. Tự thân chúng không cho bạn biết khóa đó có thuộc về đúng người mà bạn nghĩ hay không, chứng chỉ của nó có hợp lệ tại thời điểm ký hay không, hoặc sau đó nó có bị thu hồi hay không. Đó là phần việc của đường dẫn chứng chỉ và cơ chế thu hồi, được trình bày trong Xác thực một chữ ký đúng cách. Trang này cũng không giải quyết việc ký diễn ra khi nào bằng bất kỳ thẩm quyền độc lập nào. Một thời điểm ký do bản thân tự khai báo không phải là thời gian đáng tin cậy — xem Dấu thời gian và thời gian đáng tin cậy. NextPDF dựng cấu trúc được mô tả ở đây; còn các chứng chỉ, trust anchor, và cơ quan cấp dấu thời gian do triển khai của bạn cung cấp, chứ không phải engine.

Theo từng cấp độ, engine cung cấp khả năng dựng cấu trúc như sau:

PAdES signature structure (byte range, signature dictionary, detached CMS) — edition availability
Edition Availability
Core

PAdES B-B: từ điển chữ ký, ByteRange có độ rộng cố định, và đối tượng CMS SignedData tách rời được mô tả trên trang này.

Pro

Thêm PAdES B-T — một dấu thời gian đáng tin cậy trên giá trị chữ ký — trên cùng cấu trúc đó.

Enterprise

Thêm các hồ sơ dài hạn (B-LT, B-LTA): vật liệu xác thực được nhúng và dấu thời gian tài liệu được xếp chồng trên cùng nền tảng byte-range.

  • Từ điển chữ ký — từ điển PDF đặt tên cho bộ xử lý chữ ký, SubFilter, ByteRange và giá trị Contents.
  • ByteRange — một mảng các cặp số nguyên (offset, length) khai báo chính xác các byte mà digest của chữ ký bao phủ.
  • Contents — mục thập lục phân chứa giá trị chữ ký (với PDF 2.0, là một đối tượng CMS SignedData tách rời); nó bị loại trừ khỏi chính digest của mình.
  • CMS SignedData — cấu trúc Cryptographic Message Syntax (RFC 5652) chứa chứng chỉ của người ký và các byte chữ ký.
  • PAdES — PDF Advanced Electronic Signatures: hồ sơ ETSI của chữ ký CMS cho PDF, được định nghĩa trong bộ tiêu chuẩn ETSI EN 319 142.
  • Chữ ký phê duyệt — một chữ ký có quyền MDP 0; nó xác nhận nội dung mà không ràng buộc những thay đổi về sau.
  • Chữ ký chứng nhận — một chữ ký có quyền DocMDP (MDP 13) quy định những sửa đổi nào về sau vẫn giữ tài liệu ở trạng thái tuân thủ.