技術文章

HotPDF 中的 PAdES LTV 證據與種子值

您剛簽署完的 PDF 只是一個 B-B 簽章,僅此而已。它證明誰簽了名、位元組沒有被動過,但不攜帶任何簽署者憑證在簽署當下有效的證明,所以幾年後的驗證器必須去尋找可能已不存在的撤銷資料。縮小這個缺口意味著把 OCSP 回應與 CRL 寫入文件層級的 Document Security Store,而在 HotPDF 中這是一次呼叫:PopulatePAdESLTVEvidence 走訪每個已載入的簽章,從憑證集合推導撤銷請求,透過您提供的傳輸通道執行它們,並把取回的材料與 CMS 鏈寫入 DSS。它回傳證據成功落地的簽章數,或當文件完全沒有簽章欄位時回傳負一

使用它之前值得理解的設計決策是:程式庫永遠不開 socket。每一個從網路抵達的位元組,都經由您寫的回呼抵達。這不是為謹慎而謹慎;這是這個功能能在真正要求長期驗證的環境裡運作的唯一方式

為什麼程式庫拒絕自己做 HTTP?

因為要求 B-LT 簽章的地方,正是不能把網路託付給程式庫的地方。簽署服務跑在帶驗證的企業代理伺服器後面。實體隔離的簽署層沒有到回應伺服器的路由,必須餵快取的證據。稽核體制要求每個對外請求都由應用程式記錄,而不是埋在某個相依套件裡。測試套件需要確定性的回應,如果程式庫自己撥號出去,這就不可能

傳輸通道是一個固定形狀的普通函式參考,所以政策仍在您手上。HotPDF 遞給您一個描述要取什麼的請求記錄,包括內容型別與回應大小上限,您回傳位元組與狀態

HotPDF PopulatePAdESLTVEvidence 流程:呼叫端提供的 FetchEvidence 傳輸通道、請求記錄欄位與每簽章狀態結果
每個網路位元組都經過您的 FetchEvidence 回呼,每個簽章有自己的狀態,所以一次逾時永遠不會中止整輪
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 說明這是 OCSP POST 還是 CRL GET;
    // Request.ContentType 與 Request.Body 已準備好,
    // 而 Request.MaxResponseBytes 是您必須遵守的上限
    Response := HttpExchange(Request.URI, Request.ContentType,
      Request.Body, Request.MaxResponseBytes);
    Result := setsSucceeded;
  except
    on E: Exception do
    begin
      ErrorMessage := E.Message;
      // setsRetry 讓重試政策退避;404 或錯誤的 URL
      // 請改用 setsPermanentFailure
      Result := setsRetry;
    end;
  end;
end;

// 對已載入檔案中的每個簽章,一次呼叫完成 B-B 到 B-LT 的升級
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
      // 僅附加儲存:既有簽章涵蓋的位元組逐字保留
      Pdf.SaveIncrementalUpdate('signed-lt.pdf');
  finally
    Pdf.Free;
  end;
end;

失敗是按簽章計的,不是按文件計。一個對某簽署者逾時的回應伺服器,只會跳過那個簽署者的材料,其餘部分原樣保留,這正是批次處理想要的行為:部分證據勝過中止的整輪,而回傳值告訴您有多少簽章真正得到改善

CMS 忘了放進去的憑證鏈

撤銷檢查需要簽發者憑證,而多得驚人的簽章堆疊會在 CMS 容器裡遺漏中繼憑證。復原路線是 Authority Information Access 延伸,存取方法 1.3.6.1.5.5.7.48.2,它公告一個可下載簽發者憑證的 URL。HPDFFetchAIAIntermediates 透過同一個傳輸通道走訪那些 URL,從每個回應解析出 DER,只回傳 CMS 尚未攜帶的憑證,以 DER 雜湊為鍵,讓重複與迴圈都無法打轉

有兩個細節決定它對真實憑證授權單位是否管用。第一個是編碼:CA 端點以純 DER 供應憑證與以 PEM 包裝供應的頻率差不多,而且沒有可靠的內容型別可區分兩者。穩健的探測是先文字後結構:尋找 -----BEGIN CERTIFICATE----- 標記,若存在就剝掉外殼並解碼 base64;兩條路徑都要確認結果的第一個位元組是 $30,即 SEQUENCE 的 DER 標籤。第二個是深度:取回的中繼憑證自己可能為其簽發者公告 AIA URL,所以走訪會把新候選附加進佇列,補齊短了兩三跳的鏈。這必須設上限,這正是 MaxFetch 參數的用途

HotPDF 的 AIA 憑證鏈補齊圖:caIssuers URL 取回、PEM 與 DER 探測、DER 雜湊去重複與 MaxFetch 深度上限
HPDFFetchAIAIntermediates 透過同一傳輸通道走訪 caIssuers URL,探測 PEM 外殼並以 MaxFetch 為佇列設上限

什麼是簽章種子值,它為什麼會無聲失效?

種子值是文件作者附加在簽章欄位上的約束,告訴簽署者什麼樣的簽章可接受:哪個 SubFilter、哪個摘要演算法、哪些理由、哪個最低 PDF 版本、是否必須內嵌撤銷資訊。它存在欄位的 /SV 字典裡,定義於 ISO 32000-1 §12.7.5.5。HotPDF 用 AttachPAdESSeedValue 寫入它、用 CheckLoadedSignatureSeedValue 檢查它;後者在欄位無約束或每個在場約束都通過時回傳 True,在回傳 False 時透過一個輸出參數指出第一個未通過的約束,您可以直接把它放進錯誤訊息

讓種子值容易出錯的機制,是 §12.7.5.5.3 描述的 /Ff 旗標項。設為 1 的位元把對應約束標記為必要:不符即錯誤,簽署者必須拒絕。為 0 的位元把同一約束標記為偏好:該值只是過濾 UI 應該提供什麼,僅此而已。由此有兩個陷阱。第一,/Ff 住在 /SV 字典內部,不在 widget 註解上,所以讀取欄位層級 /Ff 的程式碼永遠得到空答案,進而斷定什麼都沒被強制。第二,位元配置不是簡單的一、二、四、八連續;HotPDF 的寫入端為 SubFilter 發出 2、MinVersion 發出 4、AddRevInfo 發出 32、DigestMethod 發出 64。假設連續位元的讀取器會把每個約束解碼成可選,並通過每一個測試——除了真正重要的那個

HotPDF PAdES 簽章的種子值旗標位元表,顯示 Ff 位元 2、4、32、64,以及必要與偏好約束的處理
/Ff 項位於 /SV 內部,每個位元位置決定不符是硬拒絕還是 UI 偏好
var
  Violation: AnsiString;
begin
  // 詢問欄位:我們即將用來簽署的設定檔是否被允許
  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));
  // 約束滿足:繼續簽章流程
end;

暴露原始解碼錯誤的那個測試,不是正向測試。它是一條斷言:被強制的約束不符必須被拒絕。這是唯一能抓到這類缺陷的測試:讀錯字典或讀錯位元位置的解碼器,對每個輸入都產出「沒有約束被違反」,看起來完全像正確行為,直到您刻意違反一條

它在 LTV 階梯上的位置

四個階,每一階需要它下面那一階。B-B 是裸簽章。B-T 加上受信任時間戳記,固定簽署時間,讓驗證器知道該對哪個時刻評估撤銷。B-LT 把撤銷證據加進 DSS,這正是 PopulatePAdESLTVEvidence 自動化的事。B-LTA 加上會在前一個時間戳記弱化之前續訂的文件時間戳記,無限期延伸有效性;HotPDF 以 RenewPAdESLTATimestamp 暴露它,以增量修訂附加新時間戳記,並且原樣保留每個較早的簽章、時間戳記與 DSS 項目

增量更新模型是為已簽章文件添加證據的唯一正確方式,因為重寫檔案會破壞既有簽章涵蓋的位元組範圍。如果您需要推理修訂之間改了什麼、以及那些改動是否屬於簽章允許的類型,該分析在DocMDP 與 FieldMDP 修訂分析中單獨涵蓋。簽章管線本身,包括憑證來源與位元組順序陷阱,在PAdES 簽章逐步解說中;驗證那一側在驗證已載入文件上的簽章

關於順序有一個實務警告。簽署後盡快收集證據,最好在同一個工作裡。能為某憑證作答的回應伺服器,只在憑證有效期間在線,多年後就消失了,所以一份以 B-B 離開您管線的文件,可能永遠無法再升級。HotPDF 以原生 VCL 元件為 Delphi 與 C++Builder 執行,除了您自己的傳輸通道外,整個證據流程都在同一行程內;支援的設定檔列於 HotPDF Delphi PDF component 產品頁