介紹與動機

在企業級系統中實作數位簽章時,安全性是絕對不可妥協的。
將憑證存放於本機的 PFX 或 P12 檔案雖然方便,卻會使私鑰面臨被擷取或洩漏的風險。相較之下,PKCS#11 硬體代幣(如 USB 加密狗、智慧卡與 HSM)會將金鑰保留在防篡改的邊界內,確保金鑰永不離開裝置。

本篇文章示範如何結合 GroupDocs.Signature for .NETPkcs11Interop,使用硬體代幣為 PDF 文件簽章。此方式兼具便利性與合規性:GroupDocs 處理所有 PDF 層級的封裝(簽章欄位、摘要計算、嵌入),而代幣則負責實際的密碼學簽署。

⚠️ 早期實作說明
此解決方案目前以早期實作的形式提供,用於在 GroupDocs.Signature 中使用 PKCS#11 數位簽章加密狗。
雖然它已能讓文件透過硬體代幣簽署,我們仍強烈建議在自己的環境中進行額外測試,以確保符合您的合規與安全需求。
歡迎提供回饋、測試結果與改進建議,我們將不勝感激。

挑戰:將 PKCS#11 與 PDF 簽章銜接

將 PKCS#11 代幣整合至文件簽章工作流程時,會面臨多項非平凡的挑戰:

  1. 低階複雜度 ─ PKCS#11 API(Cryptoki)需要管理槽位、會話、句柄與屬性,以找出正確的私鑰。
  2. PDF 層級封裝 ─ 簽署 PDF 不只是對位元組簽名:程式庫必須正確計算選取位元組範圍的摘要、將簽章包裝於 CMS/PKCS#7 容器、加入時間戳記,並嵌入驗證資訊。
  3. 廠商差異 ─ 不同代幣/廠商模組可能需要自訂屬性對映或額外的中介軟體。
  4. 合規與稽核 ─ 生產系統需要健全的 PIN 處理、會話生命週期管理、錯誤復原與日誌記錄。

此範例專案透過結合 GroupDocs.Signature 中的 ICustomSignHash 介面與 Pkcs11Interop,將簽署工作交給代幣,同時讓 GroupDocs 處理 PDF 結構。

範例專案的功能

  • 示範如何使用 PKCS#11 代幣(加密狗、智慧卡、HSM)簽署 PDF 文件
  • 支援 Windows 憑證存儲 後備:若 Windows 已安裝憑證,程式碼也能改用該憑證。
  • 實作 自訂雜湊簽署:GroupDocs 計算摘要;代幣僅簽署雜湊值。
  • 私鑰 始終保留在硬體上,絕不匯出。
  • 將代幣邏輯(會話、金鑰查找、簽署)封裝於 Pkcs11DigitalSigner.cs
  • 提供 Helpers.cs(例如在 Windows 存儲中查找憑證)的輔助程式碼。
  • 設定集中於 Settings.cs
  • 作為可依需求調整的參考實作。
Bridging PKCS#11 with PDF Signing

設定與先決條件

先決條件

  • .NET 6.0 或以上(或 .NET Framework 4.6.2)
  • 來自代幣廠商的有效 PKCS#11 函式庫(DLL)
  • 具備有效憑證的硬體代幣(USB 加密狗、智慧卡或 HSM)
  • GroupDocs.Signature for .NET(試用版或正式授權)
  • Pkcs11Interop 函式庫

安裝

git clone https://github.com/groupdocs-signature/esign-documents-with-pkcs11-using-groupdocs-signature-dotnet.git
cd esign-documents-with-pkcs11-using-groupdocs-signature-dotnet
dotnet restore

在 Visual Studio 或您偏好的 IDE 中開啟解決方案,確保相依性已正確解析。

Repository 結構深度說明

GroupDocs.Signature-for-.NET-PKCS11-Sample/
├── GroupDocs.Signature-for-.NET-PKCS11-Sample.csproj      # 專案檔
├── Program.cs                                             # 入口點與使用流程
├── Settings.cs                                            # PKCS#11 / 代幣設定
├── Helpers.cs                                             # 工具函式(Windows 存儲、憑證篩選)
├── Pkcs11DigitalSigner.cs                                 # 透過 PKCS#11 實作 ICustomSignHash
└── README.md                                              # 說明與使用指引
  • Program.cs ─ 協調簽署流程;示範代幣與 Windows 憑證兩種情境。
  • Settings.cs ─ 含有 Pkcs11LibraryPathTokenPinCertificateSubject 等常數/佔位符。
  • Helpers.cs ─ 包含在 Windows 存儲中依主旨名稱尋找憑證的程式碼(用於後備流程)。
  • Pkcs11DigitalSigner.cs ─ 核心邏輯:載入 PKCS#11 模組、開啟會話、定位私鑰物件、簽署摘要,並回傳 X509Certificate2 或簽章回呼實作。
  • README.md ─ 提供概觀、挑戰與使用說明(本部落格即為補充說明)。

程式碼說明與導覽

Settings.cs

public static class Settings
{
    public const string Pkcs11LibraryPath = "<PKCS11_LIBRARY_PATH>";
    public const string TokenPin = "<TOKEN_PIN>";
    public const string CertificateSubject = "<CERT_SUBJECT>";
}

此檔案將設定細節抽離,方便在部署環境中替換。

Pkcs11DigitalSigner.cs ─ 高階流程

public class Pkcs11DigitalSigner : ICustomSignHash
{
    public byte[] SignHash(byte[] hash)
    {
        // 此方法在 GroupDocs.Signature 需要代幣簽署雜湊時被呼叫
        using (var pkcs11 = new Pkcs11(Settings.Pkcs11LibraryPath, AppType.SingleThreaded))
        {
            // 載入模組、開啟會話、使用 PIN 登入、尋找金鑰並執行簽署
        }
    }

    public X509Certificate2 GetCertificateFromPkcs11()
    {
        // 從代幣取得公鑰憑證,以便設定簽章選項
    }
}
  • SignHash 為核心方法:接收 GroupDocs 計算好的摘要,然後使用 PKCS#11 API 進行簽署。
  • GetCertificateFromPkcs11 取得代幣內的憑證(含公鑰),確保簽章的元資料正確。

Program.cs ─ 使用流程

class Program
{
    static void Main()
    {
        string inputFile = "sample.pdf";
        string outputFile = "signed.pdf";

        // (1) PKCS#11 簽署
        var tokenSigner = new Pkcs11DigitalSigner();
        var cert = tokenSigner.GetCertificateFromPkcs11();

        using (var signature = new Signature(inputFile))
        {
            var options = new DigitalSignOptions(cert)
            {
                Comments = "Signed with PKCS#11 token",
                SignTime = DateTime.Now,
                CustomSignHash = tokenSigner  // 連結代幣簽署
            };
            signature.Sign(outputFile, options);
        }

        // (2) Windows 憑證存儲後備(可選)
        // var storeCert = Helpers.GetCertificateFromWindowsStore(Settings.CertificateSubject);
        // using (var signature2 = new Signature(inputFile))
        // {
        //     var options2 = new DigitalSignOptions(storeCert) { ... };
        //     signature2.Sign("signed_store.pdf", options2);
        // }
    }
}

重點說明:

  • 透過 DigitalSignOptionsCustomSignHash 屬性指派 tokenSigner,讓 GroupDocs 將實際的雜湊簽署委派給代幣。
  • 註解掉的後備流程示範在硬體代幣不可用時,如何改用 Windows 存儲中的憑證。

使用情境與實務案例

  • 印度與 CA 發行的 USB 簽章加密狗
    在印度,許多具法律效力的 eSignature 必須使用由認證機構發行、存放於 USB 加密狗的憑證。本範例讓文件閘道、入口網站等應用直接與此類加密狗整合。
  • 企業文件工作流程
    於合約管理或審批流程等內部系統中,硬體簽署可確保未授權使用者無法偽造文件簽章。
  • 法律/合規導向的簽署
    政府與受規範產業常要求簽章必須來自硬體受控金鑰,此整合有助滿足嚴格的稽核與合規需求。

常見問題與除錯

  • 函式庫路徑錯誤 → PKCS#11 DLL 必須與代幣廠商提供的模組相符(例如 softhsm2.dllcryptoki.dll)。
  • PIN 鎖定或失敗 → 重複錯誤的 PIN 可能導致代幣被鎖,請參考廠商政策。
  • 找不到金鑰 → 確認提供的憑證主旨正確;代幣必須包含相符主旨的憑證。
  • 缺少驅動或中介軟體 → 某些代幣需要先安裝廠商驅動,才能讓 Pkcs11Interop 通訊。
  • 執行緒問題 → PKCS#11 操作可能不支援多執行緒;除非廠商明確支援,請使用單執行緒模式。
  • 逾時或會話重置 → 長時間操作可能導致會話關閉或逾時,請確保正確的會話管理與資源釋放。

安全性與最佳實踐

  • 絕不要在程式碼中硬編碼生產環境的機密(PIN、函式庫路徑);使用安全的組態或祕密管理。
  • 使用 強 PIN,並在政策允許的情況下定期更換。
  • 記錄操作與錯誤(但不要記錄敏感 PIN)。
  • 限制代幣會話,簽署完畢後立即登出。
  • 簽署後驗證簽章(鏈結檢查、時間戳記)。
  • 在不同環境與代幣類型(加密狗/智慧卡/HSM)中進行測試。

後續步驟與資源

準備好自行嘗試了嗎?克隆儲存庫、更新佔位符,然後執行範例。您可能想進一步探索的主題:

  • 自訂雜湊簽署(將摘要與簽署委派給代幣)
  • 時間戳記與 LTV / DSS 嵌入
  • 迭代簽署(在同一文件中加入多個簽章)
  • 與遠端 HSM 服務或雲端代幣儲存的整合

外部連結