技術文章

HotPDF Component:數位簽章與 PAdES 簽署(Delphi)

HotPDF 是面向 Delphi 和 C++Builder 應用程式的原生 VCL PDF 程式庫,支援直接 PDF 建立、編輯、表單、註解、加密、數位簽章、Unicode 字型處理、標準導向輸出與預檢報告,無需部署外部 PDF 執行階段

當團隊需要把「數位簽章與 PAdES 簽署」交給正式環境使用時,實作必須經得起客戶檔案、操作員差異與部署環境考驗

最常見的失敗模式是樣本檔案看似正常,但真實客戶檔案、目標檢視器或下游系統暴露差異。要解決它,設計階段的決定必須能在記錄、驗證報告與支援包中被看見

把數位簽章與 PAdES 簽署寫入 PDF 處理契約

在第一行寫入程式碼之前,先把這些決定寫進設定或設計說明

  • 數位簽章與 PAdES 簽署 的輸入 PDF、範本版本、處理設定和輸出目標
  • 目標檢視器、驗證器、列印、歸檔或下游系統的相容要求
  • 權限、密碼、憑證、字型、表單、註解和標準驗證相關策略
  • 驗證失敗、警告放行、人工複核和回復路徑
  • 記錄、診斷包、暫存檔案、憑證和客戶資料的保留規則

先識別檔案狀態,再執行數位簽章與 PAdES 簽署

可維護的實作通常依照以下順序推進

  1. 先讀取來源檔案結構並確認頁數、權限、表單、簽章、字型或標準相關狀態
  2. 依命名設定執行 數位簽章與 PAdES 簽署 相關處理,並在每個階段記錄警告
  3. 生成輸出後用目標檢視器、驗證器、列印流程或下游系統複查關鍵結果
  4. 把輸入雜湊、設定版本、輸出路徑、警告數量和耗時寫入支援記錄
  5. 將暴露邊界問題的客戶 PDF 加入回歸樣本

能支援上線與排障的數位簽章與 PAdES 簽署證據

輸出結果應留下能被另一位工程師、維運人員或支援同仁讀懂的證據,而不是只依賴開發機上的人工判斷

  • 輸入檔案雜湊、頁數、設定名稱、輸出路徑和處理耗時
  • 與權限、字型、表單、簽章、標準驗證或頁面結構相關的警告
  • 目標檢視器、驗證器、列印流程或下游應用的檢查結果
  • 被保留、修改、扁平化、阻止或人工放行的物件清單
  • 支援包位置、警告數量、輸出雜湊和回歸樣本編號

數位簽章與 PAdES 簽署不是單一 PDF 呼叫

HotPDF Component 整合應把來源 PDF、頁面物件、字型、表單、簽章、權限、驗證器和目標檢視器放在同一個工程模型中。這樣 數位簽章與 PAdES 簽署 出現爭議時,團隊能解釋檔案為什麼被接受、修改、拒絕或歸檔

設定歸屬與版本管理

把 數位簽章與 PAdES 簽署 的策略做成有名稱和版本的設定,比把選項分散在多個表單、腳本或批次參數裡更容易稽核

  • 數位簽章與 PAdES 簽署 的輸入 PDF、範本版本、處理設定和輸出目標
  • 目標檢視器、驗證器、列印、歸檔或下游系統的相容要求
  • 權限、密碼、憑證、字型、表單、註解和標準驗證相關策略
  • 驗證失敗、警告放行、人工複核和回復路徑
  • 記錄、診斷包、暫存檔案、憑證和客戶資料的保留規則
  • 輸入檔案雜湊、頁數、設定名稱、輸出路徑和處理耗時
  • 與權限、字型、表單、簽章、標準驗證或頁面結構相關的警告

工程審閱要點:數位簽章與 PAdES 簽署

下面這些要點適合放進設計審查或發佈審查中,用來確認文章討論的能力不是停留在示範層面

  • 決策:數位簽章與 PAdES 簽署 的輸入 PDF、範本版本、處理設定和輸出目標。實作時重點關注 依命名設定執行 數位簽章與 PAdES 簽署 相關處理,並在每個階段記錄警告;驗收證據應包含 目標檢視器、驗證器、列印流程或下游應用的檢查結果;回歸樣本還要覆蓋 任何會改變簽章、權限或歸檔語意的步驟都必須明確記錄
  • 決策:目標檢視器、驗證器、列印、歸檔或下游系統的相容要求。實作時重點關注 生成輸出後用目標檢視器、驗證器、列印流程或下游系統複查關鍵結果;驗收證據應包含 被保留、修改、扁平化、阻止或人工放行的物件清單;回歸樣本還要覆蓋 記錄和支援包不能洩露密碼、憑證、客戶內容或暫存檔案路徑中的敏感資訊
  • 決策:權限、密碼、憑證、字型、表單、註解和標準驗證相關策略。實作時重點關注 把輸入雜湊、設定版本、輸出路徑、警告數量和耗時寫入支援記錄;驗收證據應包含 支援包位置、警告數量、輸出雜湊和回歸樣本編號;回歸樣本還要覆蓋 客戶 PDF 可能包含舊版本物件、增量更新、表單、註解、簽章或損壞參照
  • 決策:驗證失敗、警告放行、人工複核和回復路徑。實作時重點關注 將暴露邊界問題的客戶 PDF 加入回歸樣本;驗收證據應包含 輸入檔案雜湊、頁數、設定名稱、輸出路徑和處理耗時;回歸樣本還要覆蓋 不同檢視器、驗證器、列印驅動和字型環境可能改變可見結果
  • 決策:記錄、診斷包、暫存檔案、憑證和客戶資料的保留規則。實作時重點關注 先讀取來源檔案結構並確認頁數、權限、表單、簽章、字型或標準相關狀態;驗收證據應包含 與權限、字型、表單、簽章、標準驗證或頁面結構相關的警告;回歸樣本還要覆蓋 大型檔案、掃描檔案、加密檔案和帶外部資源的檔案需要單獨容量和安全策略

數位簽章與 PAdES 簽署容易暴露問題的邊界情況

這些情況最容易讓範例等級實作暴露問題,應各自納入命名回歸樣本

  • 客戶 PDF 可能包含舊版本物件、增量更新、表單、註解、簽章或損壞參照
  • 不同檢視器、驗證器、列印驅動和字型環境可能改變可見結果
  • 大型檔案、掃描檔案、加密檔案和帶外部資源的檔案需要單獨容量和安全策略
  • 任何會改變簽章、權限或歸檔語意的步驟都必須明確記錄
  • 記錄和支援包不能洩露密碼、憑證、客戶內容或暫存檔案路徑中的敏感資訊

Delphi 整合要點

在 Delphi 或 C++Builder 中,建議把 HotPDF Component 呼叫封裝在小型服務邊界後面。業務層只傳入檔案、串流、設定和憑證,服務層回傳輸出路徑、警告、指標和驗證狀態。本文涉及的重要術語包括 PAdES, digital signature, byte range, timestamp, DSS, revocation

Delphi 程式碼範例

下面的 Delphi 範例展示此主題在業務服務層中的落點。把策略檢查、記錄與驗證放在元件呼叫邊界之外,後續測試與排查會更穩定

procedure SignApprovedPdf(const InputFile, OutputFile: string; const Policy: TSignaturePolicy);
var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    LoadUnsignedPackage(Pdf, InputFile);
    CheckSigningPolicy(Policy);
    AttachPadesEvidence(Pdf, Policy.Certificate, Policy.TimestampServer);
    SaveSignedPackage(Pdf, OutputFile);
    ValidateSignatureChain(OutputFile, Policy.TrustAnchors);
  finally
    Pdf.Free;
  end;
end;

上線檢查清單

  • 分別使用空檔案、一般客戶檔案和最壞情況 PDF 執行流程
  • 用目標檢視器、驗證器、列印流程或下游系統開啟輸出結果
  • 記錄產品版本、設定版本、輸入雜湊、輸出路徑、耗時和警告數量
  • 為密碼、憑證、暫存檔案和客戶資料設定明確保留規則
  • 當客戶 PDF 暴露新邊界情況時,把樣本加入回歸集

相關產品檔案

在把該流程封裝成可重用的內部工具前,請先複查產品頁與 API 檔案: HotPDF Component

補充程式碼範例

var
  Doc: THotPDF;
  Fs: TFileStream;
  PdfBytes, HashInput, SigHex: AnsiString;
  R1Start, R1Len, R2Start, R2Len, CStart, CLen: Integer;
begin
  // 1. Write the document with a reserved /Contents hole
  Doc := THotPDF.Create(nil);
  try
    Doc.FileName := 'placeholder.pdf';
    Doc.BeginDoc;
    Doc.CurrentPage.AddSignedSignatureField('Sig1',
      Rect(50, 100, 350, 150), 8192, 'adbe.pkcs7.detached',
      'Contract approval', 'Boston, MA', 'legal@example.com');
    Doc.EndDoc;
  finally
    Doc.Free;
  end;

  // 2. Load the saved bytes; the returned offsets are 0-based
  Fs := TFileStream.Create('placeholder.pdf', fmOpenRead);
  try
    SetLength(PdfBytes, Fs.Size);
    Fs.ReadBuffer(PdfBytes[1], Fs.Size);
  finally
    Fs.Free;
  end;
  THotPDF.PreparePDFForSigning(PdfBytes, R1Start, R1Len, R2Start, R2Len,
    CStart, CLen);

  // 3. Hash both spans and sign externally (HSM, token, service)
  HashInput := Copy(PdfBytes, R1Start + 1, R1Len) +
               Copy(PdfBytes, R2Start + 1, R2Len);
  SigHex := SignWithHsm(HashInput);  // your integration: returns CMS as hex

  // 4. Splice the signature into the reserved hole
  THotPDF.InsertSignatureHex(PdfBytes, SigHex);
  Fs := TFileStream.Create('signed.pdf', fmCreate);
  try
    Fs.WriteBuffer(PdfBytes[1], Length(PdfBytes));
  finally
    Fs.Free;
  end;
end;
// PAdES baseline signature field (ETSI EN 319 142-1)
Pdf.CurrentPage.AddPAdESSignatureField(
  'ApprovalSig', Rect(50, 100, 350, 150), 'B-B',
  'Contract approval', 'Boston, MA', 'legal@example.com');

// Document timestamp: larger reservation for the TSA token and chain
Pdf.CurrentPage.AddDocumentTimestampSignature('ArchiveTS', 16384);