Introduction & Motivation

エンタープライズ向けシステムでデジタル署名を実装する際、
セキュリティは絶対条件です。\ ローカルの PFX や P12 ファイルに証明書を保存すると便利ですが、
プライベートキーが抽出・漏洩のリスクにさらされます。対照的に、
PKCS#11 ハードウェアトークン(USB ドングル、スマートカード、HSM など)は
キーを改ざん耐性のある境界内に保持し、デバイスから決して外部に出さないことを保証します。

この投稿では、GroupDocs.Signature for .NETPkcs11Interop を組み合わせて、ハードウェアトークンで PDF 文書に署名する方法を示します。
このアプローチは利便性とコンプライアンスを両立させます。GroupDocs が PDF レベルのパッケージング(署名フィールド、ダイジェスト計算、埋め込み)をすべて処理し、トークンが実際の暗号署名を行います。

⚠️ 早期実装のお知らせ\ 現在このソリューションは、PKCS#11 デジタル署名ドングルを GroupDocs.Signature と併用するための早期実装として提供されています。\ ハードウェアトークンによる文書署名は可能ですが、皆様の環境で追加テストを実施し、コンプライアンスやセキュリティ要件を満たすことをご確認いただくことを強く推奨します。\ フィードバック、テスト結果、改善提案をぜひお寄せください。

The Challenge: Bridging PKCS#11 with PDF Signing

PKCS#11 トークンを文書署名ワークフローに統合するには、いくつかの 非自明な課題 が存在します。

  1. 低レベルの複雑さ ― PKCS#11 API(Cryptoki)では、スロット、セッション、ハンドル、属性を管理して正しいプライベートキーを検索する必要があります。
  2. PDF レベルのパッケージング ― PDF への署名は単なるバイト列の署名以上の作業です。ライブラリは選択されたバイト範囲に対する正しいダイジェストを計算し、CMS/PKCS#7 コンテナに署名をラップし、タイムスタンプを付加し、検証情報を埋め込む必要があります。
  3. ベンダー差異 ― トークン/ベンダーモジュールによっては、カスタム属性マッピングや追加ミドルウェアが必要になることがあります。
  4. コンプライアンスと監査性 ― 本番システムでは、堅牢な PIN 管理、セッションライフサイクル制御、エラー回復、ロギングが求められます。

このサンプルプロジェクトは、GroupDocs.Signature の ICustomSignHash インターフェイスと Pkcs11Interop を組み合わせ、署名処理をトークンに委譲しつつ、PDF 構造の扱いは GroupDocs に任せることで上記課題に対処しています。

What the Sample Project Does

  • PDF 文書への署名 を PKCS#11 トークン(ドングル、スマートカード、HSM)で実演します。
  • Windows 証明書ストアのフォールバック をサポート:Windows に証明書がインストールされている場合は、コードがそれを使用できます。
  • カスタムハッシュ署名 を実装:GroupDocs がダイジェストを計算し、トークンがハッシュにのみ署名します。
  • プライベートキーは ハードウェア上に常駐 し、決してエクスポートされません。
  • トークンロジック(セッション、キー検索、署名)を Pkcs11DigitalSigner.cs にカプセル化。
  • Helpers.cs にヘルパーロジック(例:Windows ストアでの証明書検索)を提供。
  • 設定は Settings.cs に集中。
  • 環境に合わせてカスタマイズできる リファレンス実装 として機能します。
PKCS#11 と PDF 署名のブリッジング

Setup & Prerequisites

Prerequisites

  • .NET 6.0 以上(または .NET Framework 4.6.2)
  • トークンベンダー提供の有効な PKCS#11 ライブラリ(DLL)
  • 有効な証明書が格納されたハードウェアトークン(USB ドングル、スマートカード、または HSM)
  • GroupDocs.Signature for .NET(トライアル版または正規ライセンス)
  • Pkcs11Interop ライブラリ

Installation

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 Structure Deep Dive

GroupDocs.Signature-for-.NET-PKCS11-Sample/
├── GroupDocs.Signature-for-.NET-PKCS11-Sample.csproj      # プロジェクトファイル
├── Program.cs                                             # エントリーポイントと使用フロー
├── Settings.cs                                            # PKCS#11 / トークン設定
├── Helpers.cs                                             # ユーティリティ関数(Windows ストア、証明書フィルタリング)
├── Pkcs11DigitalSigner.cs                                 # ICustomSignHash を PKCS#11 で実装
└── README.md                                              # 概要と使用手順

-   **Program.cs** – 署名処理をオーケストレーション。トークンベースと Windows 証明書の両フローをデモ。  
-   **Settings.cs** – `Pkcs11LibraryPath`、`TokenPin`、`CertificateSubject` の定数/プレースホルダーを保持。  
-   **Helpers.cs** – サブジェクト名で Windows ストア内の証明書を検索するコードを含む(フォールバックフローで使用)。  
-   **Pkcs11DigitalSigner.cs** – コアロジック:PKCS#11 モジュールのロード、セッション開始、プライベートキーオブジェクトの検索、ダイジェスト署名、そして `X509Certificate2` または署名コールバック実装を返す。  
-   **README.md** – 本ブログ記事を補完する概要、課題、使用手順を提供。

Code Explanation & Walkthrough

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 — High-Level Flow

public class Pkcs11DigitalSigner : ICustomSignHash
{
    public byte[] SignHash(byte[] hash)
    {
        // This method is invoked by GroupDocs.Signature when it needs the token to sign a hash
        using (var pkcs11 = new Pkcs11(Settings.Pkcs11LibraryPath, AppType.SingleThreaded))
        {
            // Load module, open session, login with PIN, find key and perform signing
        }
    }

    public X509Certificate2 GetCertificateFromPkcs11()
    {
        // Retrieves the public certificate from the token so the signing options can be configured
    }
}
  • SignHash が中心的なメソッドです。GroupDocs が計算したダイジェストを受け取り、PKCS#11 API を使ってトークンで署名します。
  • GetCertificateFromPkcs11 は、署名オプション設定に必要なトークン内の公開証明書を取得します。

Program.cs — Usage Flow

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

        // (1) PKCS#11 signing
        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  // link token-based signing
            };
            signature.Sign(outputFile, options);
        }

        // (2) Windows certificate store fallback (optional)
        // 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 ストアの証明書へ切り替える例です。

Use Cases & Real-World Scenarios

  • インドの CA 発行 USB 署名ドングル\ インドでは、法的に有効な eSignature に USB ドングルに格納された証明書が必要です。このサンプルは、文書ゲートウェイやポータルといったアプリが直接ドングルと連携できるようにします。
  • エンタープライズ文書ワークフロー\ 契約管理や承認フローといった内部システムでは、ハードウェア署名により不正な文書改ざんを防止できます。
  • 法規制・コンプライアンス重視の署名\ 政府機関や規制産業では、ハードウェア制御キーからの署名が必須です。この統合により、厳格な監査・コンプライアンス要件を満たすことができます。

Common Pitfalls & Troubleshooting

  • ライブラリパスが間違っている → PKCS#11 DLL のパスはベンダー提供モジュール(例: softhsm2.dllcryptoki.dll)と一致させる必要があります。
  • PIN がロックまたは失敗 → 誤った PIN を繰り返し入力するとトークンがロックされることがあります。ベンダーのポリシーを確認してください。
  • キーが見つからない → 正しい証明書サブジェクトが指定されているか確認し、トークンに該当サブジェクトの証明書が格納されていることを確認してください。
  • ドライバやミドルウェアが不足 → 一部トークンはベンダー提供のドライバがインストールされていないと Pkcs11Interop が通信できません。
  • スレッド問題 → PKCS#11 の操作は必ずしもスレッドセーフではありません。ベンダーがマルチスレッドをサポートしない限り、シングルスレッドコンテキストを使用してください。
  • タイムアウトやセッションリセット → 長時間の処理でセッションが閉じたりタイムアウトしたりすることがあります。適切なセッション管理とクリーンアップを実装してください。

Security & Best Practices

  • 本番環境でシークレット(PIN、ライブラリパス等)をハードコードしないで、セキュアな設定やシークレット管理を利用。
  • 強力な PIN を使用し、ポリシーが許す限り定期的にローテーション。
  • 操作やエラーはログに記録する(ただし PIN など機密情報は除外)。
  • トークンセッションは必要最小限に抑え、署名後は直ちにログアウト。
  • 署名後に検証を実施(チェーンチェック、タイムスタンプ付与)。
  • 複数環境・複数トークンタイプ(ドングル/スマートカード/HSM)でテストを行う。

Next Steps & Resources

実際に試してみましょう。リポジトリをクローンし、プレースホルダーを更新したらサンプルを実行してください。次に検討できるトピック:

  • カスタムハッシュ署名(ダイジェスト+署名をトークンに委譲)
  • タイムスタンプと LTV / DSS 埋め込み
  • 反復署名(1 文書に複数署名を付与)
  • リモート HSM サービスまたはクラウドトークンストアとの統合