介紹與動機

在企業級系統實作數位簽章時,安全性是絕對不可妥協的。
將憑證儲存在本機的 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
  • 作為可依環境調整的參考實作。
在 PKCS#11 與 PDF 簽署之間搭橋

設定與前置需求

前置需求

  • .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 中開啟方案,確保相依性已解決。

リポジトリ構造深度解析

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 鎖定或驗證失敗 → 多次錯誤輸入可能導致代幣鎖定,請參考供應商政策。
  • 找不到金鑰 → 確認提供的憑證主旨正確,代幣內必須有相符的憑證。
  • 缺少驅動或中介軟體 → 某些代幣需先安裝供應商提供的驅動,才能讓 Pkcs11Interop 正常通訊。
  • 執行緒問題 → PKCS#11 操作未必具備執行緒安全;除非供應商明確支援,多數情況請使用單執行緒模式。
  • 逾時或會話重設 → 長時間操作可能導致會話關閉或逾時,請確保妥善管理會話的開啟與關閉。

安全性與最佳實務

  • 絕不要在程式碼中硬編寫正式環境的機密資訊(如 PIN、函式庫路徑);請使用安全設定或機密管理服務。
  • 使用強度高的 PIN,並遵循政策定期更換。
  • 記錄操作與錯誤(但勿記錄 PIN 等敏感資訊)。
  • 限制代幣會話數,簽署完成後立即登出。
  • 簽署後驗證簽章(鏈結檢查、時間戳記)。
  • 在不同環境與代幣類型(加密狗/智慧卡/HSM)上進行測試。

待辦事項與資源

準備好自己動手了嗎?先將倉儲複製下來,更新佔位符,然後執行範例。您可能想進一步探索的主題包括:

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

外部連結