sgcSign Server: REST 経由のセルフホスト型コード署名と文書署名

· コンポーネント

コード署名には鍵管理の問題があります。一般的な構成では、.pfx ファイルをコピーするか、USB トークンを差し込むことを、リリースに署名する必要のあるすべてのビルドエージェントに対して行います。秘密鍵は、信頼できないビルドスクリプトを実行するマシン上に置かれることになり、何が誰によって署名されたかの記録は残らず、証明書のローテーションにはすべてのエージェントへの作業が必要になります。sgcSign 2026.6 はこれに対する答えとして、新しい署名サーバーを提供します。これは TLS REST API を通じて署名を公開するセルフホスト型のデーモンであり、パイプラインや開発者がリモートで署名する一方、署名鍵はただ一か所にとどまります。

鍵がサーバーから出ることはありません。さらに良いことに、サーバーはハードウェアトークンやクラウド KMS の前面に立つことができるため、鍵がファイルとして存在することすらなくなります。すべてのリクエストは API キーで認証され、レート制限が適用され、改ざん検知可能な監査ログに記録されます。本記事では、サーバーが署名できる内容、構成方法、5 分でできるクイックスタート、そしてサーバーを呼び出す 2 つの方法、つまり素の curl と同梱の sgcsign コマンドラインクライアントについて取り上げます。

署名できるもの

1 つのサーバー、1 つの API、8 つの署名フォーマット。同じエンドポイント形状(POST /api/v1/sign/<format>)が、実行可能ファイルと文書の両方をカバーします。

すべてのフォーマットは、オプションの RFC 3161 タイムスタンプ URL を受け付けるため、署名証明書の有効期限が切れた後も署名は有効なまま保たれます。付随する POST /api/v1/verify エンドポイントは、既存の署名を検証し、署名者のサブジェクト、有効性、タイムスタンプを返します。

プラグイン可能な鍵プロバイダー

プロバイダーとは、署名鍵への名前付きハンドルです。呼び出し元はリクエストごとにプロバイダーを名前で指定しますが、鍵そのものを目にすることはありません。必要なだけ登録でき、同じサーバーで、社内ツール向けのローカル PFX と、公開リリース向けのクラウドバックされた EV 証明書を組み合わせることもできます。サポートされるプロバイダーの種類は 9 つです。

シークレットは構成ファイルの外に保たれます。名前が _env で終わるパラメーターは環境変数から読み込まれるため、PFX のパスワード、トークンの PIN、クラウドのシークレットは、ディスクにコミットされるのではなく、サービス環境から供給されます。

サーバーの構成

サーバー全体は 1 つの JSON ファイル sgcSignServer.conf.json によって駆動されます(ドキュメント付きのサンプルが sgcSignServer.conf.sample.json として同梱されています)。トップレベルの形状は、いくつかのブロックで構成されています。

{
  "server": {
    "listen": "0.0.0.0",
    "port": 8443,
    "tls": {
      "enabled": true,
      "cert_file": "certs/server.crt",
      "key_file":  "certs/server.key"
    },
    "max_upload_mb": 512,
    "firewall": {
      "enabled": true,
      "whitelist": ["10.0.0.0/8"],
      "brute_force": { "enabled": true, "max_attempts": 5, "ban_duration_sec": 900 },
      "rate_limit":  { "enabled": true, "max_connections_per_ip": 30, "time_window_sec": 60 }
    }
  },
  "storage": { "sqlite_path": "data/sgcsignserver.db" },
  "admin": {
    "initial_user": "admin",
    "initial_password_env": "SGCSIGN_ADMIN_INIT_PW",
    "session_timeout_minutes": 60
  },
  "audit": { "retention_days": 365 },
  "providers": [
    {
      "name": "pfx-build",
      "type": "PFX",
      "params": { "file": "certs/build.pfx", "password_env": "SGCSIGN_PFX_PW" }
    },
    {
      "name": "ev-release",
      "type": "AzureTS",
      "params": {
        "tenant_id": "00000000-0000-0000-0000-000000000000",
        "client_id": "00000000-0000-0000-0000-000000000000",
        "client_secret_env": "SGCSIGN_AZURE_TS_SECRET",
        "account": "your-account",
        "certificate_profile": "your-profile",
        "endpoint": "https://eus.codesigning.azure.net/"
      }
    }
  ]
}

各ブロックは機能に直接対応します。server はバインドアドレス、ポート(デフォルトは 8443)、TLS 証明書、組み込みのファイアウォール(IP の許可および拒否リスト、ブルートフォースのロックアウト、接続レート制限、パストラバーサルおよびペイロードのガード)を設定します。storage は、ユーザー、API キー、監査ログ、Webhook キューを保持する SQLite データベースを指し示します。admin は最初のユーザーとセッションの持続時間を定義します。audit は保持期間を設定します。providers は上記のリストです。

ブートストラップ用の管理者パスワードは、ファイルには決して保存されません。ごく最初の起動時、ユーザーテーブルが空のとき、サーバーは initial_password_env(デフォルトは SGCSIGN_ADMIN_INIT_PW)で指定された環境変数を読み取り、管理者アカウントを作成します。それ以降のすべての起動ではこの変数は無視されるため、最初のログインのシードとしてのみ使われます。

5 分でできるクイックスタート

サーバーをインストールし(セットアップウィザードが Windows サービスを登録するか、sgcSignServer.exe --install を実行します)、次の手順を行います。

1. テスト用のコード署名証明書を作成し、PFX としてエクスポートします。実運用では CA が発行した証明書をインポートするか、これをスキップして、代わりにプロバイダーをトークンやクラウド KMS に向けます。

$cert = New-SelfSignedCertificate -Type CodeSigningCert `
  -Subject "CN=sgcSign Quickstart" -KeyAlgorithm RSA -KeyLength 3072 `
  -CertStoreLocation Cert:\CurrentUser\My -NotAfter (Get-Date).AddYears(1)
$pw = ConvertTo-SecureString "QuickStartPFX!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -Password $pw `
  -FilePath "C:\Program Files\sgcSign Server\certs\quickstart.pfx"

2. シークレットをマシンの環境変数として設定し、サービスアカウントが読み取れるようにします。

setx /M SGCSIGN_ADMIN_INIT_PW "ChangeMeNow!"
setx /M SGCSIGN_PFX_PW        "QuickStartPFX!"

3. 最小限の構成を記述します。1 つの PFX プロバイダーを使います(TLS はローカルホストのテストのためだけにオフにしています)。

{
  "server":   { "listen": "127.0.0.1", "port": 8443, "tls": { "enabled": false } },
  "storage":  { "sqlite_path": "data/sgcsignserver.db" },
  "admin":    { "initial_user": "admin", "initial_password_env": "SGCSIGN_ADMIN_INIT_PW" },
  "audit":    { "retention_days": 365 },
  "providers": [
    { "name": "pfx-quickstart", "type": "PFX",
      "params": { "file": "certs/quickstart.pfx", "password_env": "SGCSIGN_PFX_PW" } }
  ]
}

4. サービスを開始し、リッスンしていることを確認します。

sc start sgcSignServer
sc query sgcSignServer

起動時に停止する場合は、sgcSignServer.exe --console --config <path> でフォアグラウンド実行し、エラーをリアルタイムで確認します(SGCSIGN_ADMIN_INIT_PW の欠落や読み取り不能な PFX が一般的な原因です)。--selftest-providers フラグは構成を読み込み、すべてのプロバイダーを初期化して終了するため、本番投入前に鍵を検証できます。

5. ログインして API キーを作成します。http://localhost:8443/admin を開き、ブートストラップ用のパスワードで admin としてサインインし、API キーを作成します。平文のキーは作成時に一度だけ表示されるので、すぐにコピーしてください。キーには sgcsk_ というプレフィックスが付きます。

6. テスト用の実行可能ファイルに署名します。curl を使います。

curl -X POST http://localhost:8443/api/v1/sign/authenticode ^
  -H "X-API-Key: sgcsk_..." ^
  -F file=@app.exe ^
  -F provider=pfx-quickstart ^
  -F hash=sha256 ^
  --output app-signed.exe

これがループ全体です。プロバイダーを構成し、キーを発行し、署名する。ここから先は、自己署名 PFX を実際の証明書に差し替え、TLS をオンにし、ファイアウォールを CI サブネットに限定して固めます。

REST 経由の署名

すべての署名リクエストは、ファイルといくつかのフォームフィールドを伴う POST です。認証はヘッダー 1 つで、X-API-Key: sgcsk_... または Authorization: Bearer sgcsk_... のいずれかです。レスポンスは application/octet-stream としての署名済みアーティファクトであり、署名者のサブジェクトと署名にかかった時間が X-Sgcsign-Signer-Subject および X-Sgcsign-Duration-Ms ヘッダーで返されます。

PDF への署名。署名の理由と場所を可視化します。

curl -X POST https://sign.acme.local:8443/api/v1/sign/pades \
  -H "X-API-Key: sgcsk_..." \
  -F file=@invoice.pdf \
  -F provider=qualified-eu \
  -F tsa_url=http://timestamp.digicert.com \
  -F reason=Approved \
  -F location="Madrid, Spain" \
  -F signer_name="Acme Billing" \
  -o invoice-signed.pdf

eIDAS XAdES プロファイルでの XML 請求書への署名。

curl -X POST https://sign.acme.local:8443/api/v1/sign/xades \
  -H "X-API-Key: sgcsk_..." \
  -F file=@invoice.xml \
  -F provider=qualified-eu \
  -F profile=eidas \
  -F xades_type=enveloped \
  -o invoice-signed.xml

署名の検証は、ファイルではなく JSON を返します。

curl -X POST https://sign.acme.local:8443/api/v1/verify \
  -H "X-API-Key: sgcsk_..." \
  -F file=@app-signed.exe \
  -F format=authenticode

{ "valid": true, "status": "valid",
  "subject": "CN=Acme Inc., O=Acme, C=US",
  "has_timestamp": true, "timestamp_time": "2026-06-15T13:01:17.000Z" }

sgcsign コマンドラインクライアント

サーバーには sgcsign が同梱されています。これは signverifykeyshealth という 4 つの動詞を持つ、フォーマット横断の小さな CLI です。サーバー URL、API キー、プロバイダーは、フラグから渡すか、環境変数 SGCSIGN_SERVERSGCSIGN_APIKEYSGCSIGN_PROVIDER から渡すことができ、CI でシークレットをコマンドラインに乗せずに済みます。

set SGCSIGN_SERVER=https://sign.acme.local:8443
set SGCSIGN_APIKEY=sgcsk_...

sgcsign sign -f authenticode -p pfx-build -o app-signed.exe app.exe ^
  --tsa http://timestamp.digicert.com --desc "Acme Installer"

sgcsign verify -f authenticode app-signed.exe

Authenticode には帯域を節約するための工夫があります。プリハッシュモード(--prehash、デフォルトでオン)では、CLI が PE のハッシュをローカルで計算し、ハッシュだけ、つまり数百バイトだけをアップロードします。数メガバイトのバイナリではありません。サーバーはハッシュに署名し、detached の PKCS#7 ブロブを返し、クライアントがそれをファイルに埋め込みます。帯域が制限された CI エージェントで大きなインストーラーに署名する場合、数メガバイトのアップロードがごくわずかなものに変わります。

セキュリティと運用

このサーバーは、実際の署名鍵の前面で無人運用されることを前提に作られているため、運用面はクリプトと同じくらい重要です。

セルフホストする理由

このサーバーの狙いは、鍵を抱えるリスクの高いビルドエージェント群を、堅牢化された単一のエンドポイントに集約することにあります。署名鍵はサーバー上、あるいはサーバーが前面に立つ HSM やクラウド KMS の中にのみ存在し、ビルドスクリプトを実行するマシン上には決して置かれません。アクセスはクォータ付きのキーごとの API トークンでゲートされ、プロジェクトにスコープされ、すべての署名はあなたが管理する監査ログに記録されます。証明書のローテーション、侵害されたエージェントのキーの失効、誰が何に署名したかの証明、これらすべてが、複数箇所ではなく一か所を見れば済むようになります。

提供状況

sgcSign Server は sgcSign 2026.6.0 の一部です。Windows サービスまたはコンソールアプリケーションとして動作し、上記の単一 JSON ファイルで構成され、curl、同梱の sgcsign CLI、または OpenAPI 記述を通じた任意の HTTP クライアントから駆動できます。

ご質問、フィードバック、セットアップのお手伝いが必要ですか。お問い合わせください。コードを書いた本人から返信が届きます。