Bỏ qua để đến nội dung
getnextpdf.com

Xác thực chữ ký đúng cách

Spec: RFC 5280, §6Spec: RFC 6960Spec: RFC 5652

“Chữ ký hợp lệ” thường chỉ có nghĩa là một điều đã được kiểm tra: kết quả tính toán khớp. Một quá trình xác thực đúng cách kiểm tra ít nhất năm điều độc lập, và bất kỳ điều nào trong số đó cũng có thể thất bại theo cách khiến dấu tích màu xanh trở nên vô nghĩa. Trang này trình bày đầy đủ tập hợp các bước kiểm tra, đồng thời giải thích vì sao một câu trả lời chưa đầy đủ lại nguy hiểm.

Một giá trị boolean đơn lẻ là kết quả nguy hiểm nhất trong toàn bộ chủ đề này. Nó khiến người đọc xem “hợp lệ” là “đáng tin cậy”, trong khi “hợp lệ” có thể chỉ có nghĩa là các byte không bị thay đổi — bởi một khóa từ một chứng chỉ đã hết hạn ba năm trước, đã bị thu hồi tháng trước, và chuỗi tới một thẩm quyền mà bạn không công nhận. Mỗi điều trong số đó là một bước kiểm tra riêng biệt. Phần mềm chỉ báo cáo một giá trị boolean đã âm thầm quyết định những bước kiểm tra nào là quan trọng, và quyết định thay cho bạn. Trong môi trường chịu quy định hoặc ràng buộc hợp đồng, “công cụ báo hợp lệ” không phải là một lý lẽ biện hộ nếu công cụ chỉ xác minh thuộc tính ít tốn kém nhất.

Một quá trình xác thực đầy đủ trả lời năm câu hỏi riêng biệt. Chúng độc lập với nhau — vượt qua một câu hỏi không nói lên điều gì về các câu hỏi còn lại:

  1. Tính toàn vẹn — các byte đã ký có còn tạo ra giá trị băm khớp với những gì chữ ký bao phủ không? (Tính lại bản tóm tắt băm của dải byte; so sánh.)
  2. Tính xác thực — chữ ký mật mã có xác minh được với khóa công khai trong chứng chỉ ký, trên các thuộc tính đã ký không?
  3. Đường dẫn chứng chỉ — chứng chỉ đó có chuỗi tới một điểm tin cậy gốc mà bạn chọn, với mọi mắt xích đều hợp lệ không?
  4. Thời gian — chứng chỉ có nằm trong khoảng hiệu lực của nó tại thời điểm liên quan không, và thời điểm đó có đáng tin cậy thay vì do bên ký tự khẳng định không?
  5. Thu hồi — chứng chỉ có chưa bị thu hồi tại thời điểm đó không, dựa trên bằng chứng (OCSP/CRL) mà bạn thực sự có thể lấy được hoặc đã được nhúng sẵn?

Một kết quả “hợp lệ” không thực hiện cả năm bước là một câu trả lời chưa đầy đủ nhưng trông như đã đầy đủ.

Quan điểm của NextPDF là mỗi câu hỏi đều riêng biệt và mỗi câu hỏi đều phải được trả lời rõ ràng. Một câu hỏi không bao giờ bị gộp lại thành một cờ lạc quan duy nhất, và không bao giờ bị âm thầm bỏ qua chỉ vì một bước kiểm tra bất tiện. Điều này được các bài kiểm thử bảo đảm thực thi. Đó là lý do vì sao trang này được đánh dấu là dựa trên kiểm thử thay vì dựa trên tiêu chuẩn: hành vi này được bộ kiểm thử giữ cố định, chứ không chỉ được suy luận từ một điều khoản.

Tính toàn vẹn và tính xác thực được kiểm thử từ đầu đến cuối. Một chứng chỉ đã biết trước ký lên một cấu trúc thuộc tính đã ký thực sự, và bộ kiểm thử xác minh chữ ký bằng khóa công khai tương ứng qua nhiều mốc thời gian. Vì vậy, một thay đổi làm hỏng cấu trúc chuẩn tắc sẽ làm hỏng bài kiểm thử. Việc xác thực đường dẫn chứng chỉ được giữ vững bằng các bài kiểm thử cố tình sửa đổi một byte chữ ký và khẳng định kết quả là không hợp lệ, kèm theo một lý do có cấu trúc — không phải một ngoại lệ bị bỏ qua, mà là một thất bại được ghi nhận rõ ràng. Việc xác minh token dấu thời gian được chia thành các bước riêng rẽ — giải mã, thông tin về bên ký, thuộc tính đã ký, bản tóm tắt thông điệp, ràng buộc chứng chỉ, mục đích sử dụng khóa, chữ ký, thời điểm tạo — và mỗi bước được kiểm thử riêng, nên “dấu thời gian đã được xác minh” có nghĩa là mọi bước đều đã được xác minh. Đối với một dấu thời gian chữ ký, một trong những bước đó không phải là tùy chọn và không mang tính chung chung: dấu ấn thông điệp (message imprint) của token phải ràng buộc với chữ ký mà nó tuyên bố bao phủ — giá trị băm bên trong token phải bằng giá trị băm của chính giá trị chữ ký của bên ký, nếu không token đang chứng thực cho các byte khác. NextPDF phơi bày điều này dưới dạng một bước kiểm tra chuyên biệt verifySignatureTimestamp nhận giá trị chữ ký làm đầu vào bắt buộc, nên nó không thể bị bỏ qua, và thất bại đóng (fail closed) khi gặp đầu vào rỗng, một thuật toán dấu ấn không được nhận diện hoặc yếu (SHA-1), một dấu ấn bị lỗi định dạng, hoặc một giá trị băm không khớp. Lỗi mềm khi kiểm tra thu hồi (không liên hệ được với bên phản hồi) được phân biệt, trong mã nguồn và trong các bài kiểm thử, với kết quả dứt khoát “đã bị thu hồi”. Hai điều này không bao giờ bị gộp chung thành cùng một câu trả lời.

  1. IntegrityRecompute the byte-range digest and compare it to the value the signature covers.
  2. AuthenticityVerify the cryptographic signature against the certificate’s public key, over the signed attributes — not the raw content.
  3. Certificate pathBuild and validate the chain to a trust anchor you chose; every link’s signature, validity, and constraints must hold.
  4. TimeConfirm the certificate was valid at the relevant instant, and that the instant is trusted time, not the signer’s clock.
  5. RevocationConfirm the certificate was not revoked at that time, using obtainable or embedded OCSP/CRL evidence.
Năm câu hỏi độc lập mà một quá trình xác thực chữ ký PDF đúng cách trả lời, theo thứ tự. Mỗi câu hỏi đều riêng biệt: vượt qua một câu hỏi không nói lên điều gì về các câu hỏi còn lại, và bỏ qua bất kỳ câu hỏi nào cũng khiến kết quả tổng thể trở nên chưa đầy đủ.

Phần đáng học hỏi không phải là một lệnh gọi API. Đó là những câu hỏi mà bạn phải trả lời được trước khi hành động dựa trên một kết quả. Hãy xem đây là danh sách kiểm tra mà một cuộc rà soát sẽ buộc bạn tuân thủ.

<?php
declare(strict_types=1);
// A correct validation produces a structured outcome, not one boolean.
// Before you trust a signature, you must be able to answer ALL of these:
//
// integrity : Does the byte-range digest still match? (tamper check)
// authenticity: Does the signature verify over the SIGNED ATTRIBUTES,
// not just the content?
// path : Does the certificate chain to a trust anchor YOU chose,
// with every link valid at the relevant time?
// time : Is the relevant time TRUSTED (a timestamp), or merely the
// signer's self-asserted clock?
// revocation : Was the certificate not revoked at that time, by evidence
// you obtained or that the document embedded?
//
// "valid: true" without an answer to every line above is an incomplete
// result. A path-validation outcome carries a `valid` flag AND a structured
// `reasons` list precisely so a failure says WHY — never a bare false.

Nếu bất kỳ dòng nào có câu trả lời là “Tôi không biết”, thì trạng thái trung thực không phải là “hợp lệ.” Nó là “không xác định” — và việc xem hai trạng thái này là một chính là sai lầm mà trang này tồn tại để ngăn chặn.

Cạm bẫy là đánh đồng “hợp lệ về mặt mật mã” với “đáng tin cậy.” Tính toàn vẹn và tính xác thực cùng nhau chỉ chứng minh những byte này đã được ký bởi người nắm giữ khóa này. Chúng không nói lên điều gì về việc chứng chỉ của khóa đó có được tin cậy, còn hiệu lực hay chưa bị thu hồi hay không. Một tài liệu được ký bằng chứng chỉ tự tạo có thể “hợp lệ về mặt mật mã” nhưng không mang giá trị tin cậy nào. Cạm bẫy ngược lại là xem một bước kiểm tra thu hồi không xác định (bên phản hồi ngoại tuyến) là đạt — hoặc là trượt. Nó không thuộc trường hợp nào. Nó là chưa biết, và một bộ xác thực đúng cách sẽ báo cáo nó là chưa biết thay vì đoán theo bất kỳ hướng nào. Một dấu tích màu xanh che giấu việc bước nào trong số năm bước kiểm tra thực sự đã chạy không phải là một kết quả xác thực. Đó là một quyết định mà người khác đã thay bạn đưa ra.

NextPDF thực hiện và kiểm thử các bước kiểm tra về cấu trúc và mật mã. Nó không chọn các điểm tin cậy gốc cho bạn và cũng không bảo đảm các chính sách được xây dựng trên đó. Việc bạn tin cậy những chứng chỉ nào là một quyết định triển khai mà engine không thể đưa ra. Một chuỗi xác thực được tới một điểm tin cậy gốc mà lẽ ra bạn không nên tin cậy vẫn là một kết quả xác thực mà bạn không thể dựa vào. Bằng chứng thu hồi chỉ có thể được kiểm tra nếu có thể lấy được hoặc đã được nhúng sẵn. Một bên phản hồi ngoại tuyến dẫn tới kết quả “không xác định”, và việc biến điều đó thành một phán quyết là một lựa chọn chính sách, không phải quyết định của engine. Trang này mô tả tập hợp các bước kiểm tra, không phải sự đầy đủ về mặt pháp lý. Liệu một chữ ký đã được xác thực có hiệu lực pháp lý cụ thể hay không tùy thuộc vào chứng chỉ, bên ký, thẩm quyền tài phán và nghĩa vụ. Cách bằng chứng được nhúng giữ cho các bước kiểm tra này vẫn trả lời được theo thời gian được trình bày trong Xác thực dài hạn; cơ chế dải byte đằng sau bước kiểm tra tính toàn vẹn nằm trong Chữ ký nằm ở đâu trong một tệp PDF.

Mức độ khả dụng theo bậc của bề mặt xác thực:

Signature validation checks — edition availability
EditionAvailability
Core

Tính toàn vẹn và tính xác thực trên các thuộc tính đã ký, cùng với việc xác thực đường dẫn chứng chỉ theo RFC 5280 §6 dựa trên một điểm tin cậy gốc được cung cấp.

Pro

Bổ sung việc xác minh token dấu thời gian RFC 3161 — câu hỏi về thời gian đáng tin cậy, được phân tách thành các bước được kiểm tra độc lập.

Enterprise

Bổ sung việc đánh giá thu hồi (OCSP/CRL) và xác thực dựa trên vật liệu dài hạn được nhúng, với các kết quả không xác định được phân biệt với các kết quả dứt khoát.

  • Kiểm tra tính toàn vẹn — tính lại bản tóm tắt băm của dải byte và so sánh nó với giá trị mà chữ ký bao phủ.
  • Kiểm tra tính xác thực — xác minh chữ ký mật mã với khóa công khai của chứng chỉ ký, trên các thuộc tính đã ký.
  • Thuộc tính đã ký — các thuộc tính CMS đã được xác thực (content-type, message-digest, signing-time, signing-certificate-v2) mà chữ ký thực sự được tính toán trên các thuộc tính đó.
  • Xác thực đường dẫn chứng chỉ — xây dựng và kiểm tra chuỗi từ chứng chỉ ký tới một điểm tin cậy gốc được chọn (RFC 5280 §6).
  • Điểm tin cậy gốc — một thẩm quyền chứng chỉ mà bạn đã quyết định tin cậy; gốc của một đường dẫn chấp nhận được.
  • Kiểm tra thu hồi — xác định xem một chứng chỉ có bị thu hồi tại thời điểm liên quan hay không, qua OCSP hoặc một CRL.
  • Không xác định — một kết quả thu hồi không phải là “tốt” cũng không phải là “đã bị thu hồi” vì không thể lấy được bằng chứng; không phải đạt cũng không phải trượt.
  • Ràng buộc dấu ấn (dấu thời gian chữ ký) — bước kiểm tra rằng dấu ấn thông điệp của một dấu thời gian chữ ký bằng với giá trị băm của giá trị chữ ký SignerInfo của bên ký mà nó bao phủ, để dấu thời gian có thể chứng minh được là áp dụng cho chữ ký đó thay vì cho các byte khác.