Bài viết kỹ thuật

Bằng chứng LTV PAdES và seed value trong HotPDF

Một PDF bạn vừa ký là một chữ ký B-B và không hơn. Nó chứng minh ai đã ký và các byte chưa dịch chuyển, nhưng nó không mang bằng chứng rằng chứng chỉ người ký đã hợp lệ tại thời điểm ký, nên một trình xác minh nhiều năm sau phải đi tìm dữ liệu thu hồi mà có thể không còn tồn tại. Lấp khoảng trống đó nghĩa là ghi các phản hồi OCSP và CRL vào Document Security Store cấp tài liệu, và trong HotPDF đó là một lệnh gọi: PopulatePAdESLTVEvidence đi qua mọi chữ ký đã nạp, suy ra các yêu cầu thu hồi từ tập chứng chỉ, thực thi chúng qua một transport bạn cung cấp, và ghi vật liệu đã lấy cùng chuỗi CMS vào DSS. Nó trả về số chữ ký mà bằng chứng đã đến nơi, hoặc trừ một khi tài liệu chẳng có trường chữ ký nào

Quyết định thiết kế đáng hiểu trước khi dùng nó là thư viện chưa bao giờ mở một socket. Mọi byte đến từ mạng đều đến qua một callback bạn viết. Điều đó không phải sự dè chừng để mà dè chừng; đó là cách duy nhất để tính năng này hoạt động được bên trong những môi trường thực sự đòi hỏi xác thực dài hạn

Vì sao thư viện từ chối tự làm HTTP của riêng mình?

Vì những nơi đòi hỏi chữ ký B-LT là những nơi không thể trao mạng cho một thư viện tin cậy được. Các dịch vụ ký chạy sau proxy xác thực với gốc doanh nghiệp. Các tầng ký cách ly mạng không có đường tới responder và phải được cho ăn bằng chứng đã cache. Các chế độ kiểm toán đòi hỏi mọi yêu cầu gửi ra phải được ứng dụng ghi log, chứ không chôn trong một phụ thuộc. Và các bộ kiểm thử cần phản hồi xác định, điều bất khả nếu thư viện tự mình quay số ra ngoài

Transport là một tham chiếu hàm giản dị với hình dạng cố định, nên chính sách vẫn thuộc về bạn. HotPDF đưa cho bạn một record yêu cầu mô tả chính xác cái cần lấy, gồm kiểu nội dung và một trần kích thước phản hồi, và bạn trả về các byte cùng một trạng thái

Luồng PopulatePAdESLTVEvidence của HotPDF: transport FetchEvidence do caller cung cấp, các trường record yêu cầu và kết quả trạng thái theo từng chữ ký
Mọi byte mạng đều đi qua callback FetchEvidence của bạn, và mỗi chữ ký có trạng thái riêng nên một lần timeout chưa từng hủy cả lượt
function FetchEvidence(const Request: THPDFSignatureEvidenceRequest;
  Attempt: Integer; CancellationToken: THPDFCancellationToken;
  out Response: TBytes; out RetryAfterMS: Cardinal;
  out ErrorMessage: UnicodeString): THPDFSignatureEvidenceTransportStatus;
begin
  RetryAfterMS := 0;
  try
    // Request.Kind cho biết đây là OCSP POST hay CRL GET;
    // Request.ContentType và Request.Body đã được chuẩn bị sẵn,
    // và Request.MaxResponseBytes là trần mà bạn phải tôn trọng
    Response := HttpExchange(Request.URI, Request.ContentType,
      Request.Body, Request.MaxResponseBytes);
    Result := setsSucceeded;
  except
    on E: Exception do
    begin
      ErrorMessage := E.Message;
      // setsRetry cho chính sách thử lại lùi lại; dùng
      // setsPermanentFailure cho một 404 hoặc một URL xấu
      Result := setsRetry;
    end;
  end;
end;

// Nâng B-B lên B-LT một lệnh gọi cho mọi chữ ký trong tệp đã nạp
var
  Pdf: THotPDF;
  Upgraded: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.BeginIncrementalUpdate('signed.pdf');
    Upgraded := Pdf.PopulatePAdESLTVEvidence(FetchEvidence,
      THPDFSignatureEvidenceRetryPolicy.Default);
    if Upgraded > 0 then
      // Lưu chỉ-nối-thêm: các byte mà những chữ ký hiện có phủ
      // được giữ nguyên từng chữ
      Pdf.SaveIncrementalUpdate('signed-lt.pdf');
  finally
    Pdf.Free;
  end;
end;

Thất bại theo từng chữ ký, không phải theo tài liệu. Một responder timeout với một người ký sẽ bỏ qua vật liệu của người ký đó và để nguyên phần còn lại của lượt, đúng hành vi bạn muốn trong một lô: bằng chứng một phần vẫn hơn một lượt chạy bị hủy, và giá trị trả về cho biết bao nhiêu chữ ký thực sự được cải thiện

Chuỗi mà CMS quên không kèm theo

Kiểm tra thu hồi cần chứng chỉ của bên cấp, và một số lượng gây bất ngờ các hệ ký bỏ sót các intermediate trong container CMS. Con đường phục hồi là phần mở rộng Authority Information Access, phương thức truy cập 1.3.6.1.5.5.7.48.2, thứ quảng bá một URL nơi có thể tải chứng chỉ của bên cấp xuống. HPDFFetchAIAIntermediates đi qua những URL đó bằng cùng transport, phân tích DER từ mỗi phản hồi, và chỉ trả về các chứng chỉ mà CMS chưa mang sẵn, khóa theo hash DER để trùng lặp và vòng lặp không thể quay vô hạn

Hai chi tiết quyết định điều này có hiệu lực với các nhà cấp chứng chỉ thật hay không. Thứ nhất là mã hóa: các đầu cuối CA phục vụ chứng chỉ dưới dạng DER trần cũng thường như phục vụ dưới dạng bọc PEM, và không có kiểu nội dung đáng tin nào để phân biệt chúng. Cách dò chắc chắn là theo văn bản trước, rồi theo cấu trúc. Tìm marker -----BEGIN CERTIFICATE-----, lột lớp bọc và giải mã base64 nếu có, và trong cả hai đường đều xác nhận byte đầu của kết quả là $30, thẻ DER của một SEQUENCE. Thứ hai là độ sâu: một intermediate lấy về có thể tự quảng bá một URL AIA cho bên cấp của chính nó, nên bước đi nối các ứng viên mới vào hàng đợi và hoàn tất các chuỗi còn thiếu hai ba chặng. Phải chặn trần, và đó là mục đích của tham số MaxFetch

Sơ đồ hoàn tất chuỗi AIA cho HotPDF: lấy URL caIssuers, dò PEM so với DER, khử trùng lặp theo hash DER và trần độ sâu MaxFetch
HPDFFetchAIAIntermediates đi qua các URL caIssuers bằng cùng transport, dò lớp bọc PEM và chặn trần hàng đợi bằng MaxFetch

Seed value chữ ký là gì, và vì sao nó thất bại một cách câm lặng?

Một seed value là ràng buộc mà tác giả tài liệu gắn vào một trường chữ ký để bảo người ký loại chữ ký nào được chấp nhận: SubFilter nào, thuật toán digest nào, các lý do nào, phiên bản PDF tối thiểu nào, thông tin thu hồi có phải được nhúng hay không. Nó nằm trong một từ điển /SV trên trường và được định nghĩa tại ISO 32000-1 §12.7.5.5. HotPDF ghi nó bằng AttachPAdESSeedValue và kiểm tra bằng CheckLoadedSignatureSeedValue, hàm trả về True khi trường không bị ràng buộc hoặc mọi ràng buộc hiện diện đều qua, và khi False nêu tên ràng buộc thất bại đầu tiên qua một tham số đầu ra bạn có thể đặt thẳng vào một thông điệp lỗi

Cơ chế khiến seed value dễ làm sai là mục cờ /Ff được mô tả tại §12.7.5.5.3. Một bit bật đánh dấu ràng buộc của nó là bắt buộc: một sự lệch là lỗi và người ký phải từ chối. Một bit tắt đánh dấu cùng ràng buộc đó là sở thích: giá trị lọc cái UI nên cung cấp và không hơn. Hai cái bẫy đi theo từ đó. Thứ nhất, /Ff nằm bên trong từ điển /SV, không nằm trên annotation widget, nên mã đọc /Ff cấp trường sẽ nhận câu trả lời rỗng mãi mãi và kết luận chẳng có gì bị ép. Thứ hai, các chỉ định bit không phải là chuỗi đơn giản một, hai, bốn, tám; trong HotPDF, trình ghi phát 2 cho SubFilter, 4 cho MinVersion, 32 cho AddRevInfo và 64 cho DigestMethod. Một trình đọc giả định các bit tuần tự sẽ giải mã mọi ràng buộc là tùy chọn và qua mọi phép thử, trừ đúng phép thử quan trọng

Bảng bit cờ seed value cho ký PAdES HotPDF cho thấy các bit Ff 2, 4, 32 và 64 cùng cách xử lý ràng buộc bắt buộc so với ưa thích
Mục /Ff nằm bên trong /SV, và từng vị trí bit quyết định một sự lệch là từ chối dứt khoát hay chỉ là sở thích UI
var
  Violation: AnsiString;
begin
  // Hỏi trường xem hồ sơ chúng ta sắp ký có được phép không
  if not Pdf.CheckLoadedSignatureSeedValue(0, 'ETSI.CAdES.detached',
       'SHA256', 'Approved for payment', 1, Violation) then
    raise Exception.Create('Signature field rejects this profile: ' +
      String(Violation));
  // Ràng buộc đã thỏa: tiếp tục lượt ký
end;

Phép thử làm lộ con bug giải mã ban đầu không phải là phép thử khẳng định chiều thuận. Nó là khẳng định rằng một sự lệch bị ép buộc phải bị từ chối, và đó là kiểu phép thử duy nhất bắt được lớp khuyết tật này: một bộ giải mã đọc nhầm từ điển hoặc nhầm các vị trí bit sẽ tạo ra "không ràng buộc nào bị vi phạm" cho mọi đầu vào, thứ trông giống hệt hành vi đúng cho đến khi bạn cố tình vi phạm một ràng buộc

Chỗ này nằm ở bậc nào trên thang LTV

Bốn bậc, và mỗi bậc cần bậc dưới nó. B-B là chữ ký trần trụi. B-T thêm một dấu thời gian đáng tin cậy, thứ cố định thời điểm ký để trình xác minh biết đánh giá thu hồi vào khoảnh khắc nào. B-LT thêm bằng chứng thu hồi vào DSS, điều mà PopulatePAdESLTVEvidence tự động hóa. B-LTA thêm các dấu thời gian tài liệu được gia hạn trước khi dấu trước đó suy yếu, kéo dài hiệu lực vô hạn định; HotPDF phơi điều đó thành RenewPAdESLTATimestamp, hàm nối một dấu thời gian mới như một bản sửa gia tăng và giữ nguyên từng chữ ký, dấu thời gian và mục DSS trước đó không sứt mẻ

Mô hình cập nhật gia tăng là cách đúng duy nhất để thêm bằng chứng vào một tài liệu đã ký, vì viết lại tệp sẽ phá các khoảng byte mà những chữ ký hiện có phủ. Nếu bạn cần lý luận về cái gì đã thay đổi giữa các bản sửa, và những thay đổi đó có thuộc loại mà một chữ ký cho phép hay không, phân tích đó được đề cập riêng trong phân tích sửa đổi DocMDP và FieldMDP. Bản thân pipeline ký, gồm các nguồn chứng chỉ và các cạm bẫy thứ tự byte, nằm trong hướng dẫn từng bước ký PAdES, còn phía xác minh nằm trong xác minh chữ ký trên tài liệu đã nạp

Một cảnh báo thực tiễn về thứ tự. Hãy thu thập bằng chứng càng sớm càng tốt sau khi ký, lý tưởng nhất là trong cùng một tác vụ. Các responder trả lời được cho một chứng chỉ chỉ online trong khi chứng chỉ còn hiện hành rồi biến mất nhiều năm sau, nên một tài liệu rời pipeline của bạn ở mức B-B có thể sẽ chẳng bao giờ nâng cấp được nữa. HotPDF chạy như một thành phần VCL thuần cho Delphi và C++Builder, và toàn bộ lượt lấy bằng chứng chạy trong tiến trình, trừ transport của chính bạn; các hồ sơ được hỗ trợ được liệt kê trên trang sản phẩm HotPDF Delphi PDF component