GitHub Actionsで構築するWindowsコード署名環境:Sectigo × Google Cloud KMS編
概要
Sectigoのコード署名証明書とGoogle Cloud KMSのHSMを組み合わせ、GitHub Actions上でWindowsアプリのバイナリに自動でコード署名できる環境を構築した。
署名処理を自社管理のGoogle Cloud KMSで行うことで、署名数に応じた従量課金の負担を軽減し、柔軟な運用を実現している。
本記事では、その背景と構成、具体的な構築手順について詳しく解説する。
GitHub Actionsのコード署名環境のシステム構成図
GitHub ActionsのWindows Runner上に構築した署名環境は下図のような構成である。
SignTool.exeでの署名処理は、Googleが提供するKMS CNG(Cryptography Next Generation)Providerを介してHSM上にある鍵を用いて実行される。これにより、秘密鍵をGitHub Actions上に保持することなく、安全に署名処理を実行できる。
なぜSectigoのコード署名証明書を選んだか
理由は、必要以上にコストをかけずにコード署名環境を構築できるためである。
例えば、競合のSSL.comが提供するコード署名証明書は、署名回数に応じた従量課金が発生する。この従量課金が意外と重くのしかかる。
以下スクリーンショットはSSL.comのコード署名の料金表だが、1ヶ月あたり20署名で20USD/月は一見十分な量に思えるかもしれない。しかし、実運用では本番環境と検証環境が分かれていることが多く、検証用バイナリにも都度署名を行うような運用を考えると、20署名はすぐに使い切ってしまう。結果として、100署名あるいは300署名といった上位プランを契約する必要が生じ、毎月それなりの金額を支払うことになる。
SSL.comを例に挙げたが、ベンダー提供の署名システムを利用する場合はどこも似たような従量課金の仕方をしている。
一方、Sectigoコード署名は、自社管理のGoogle Cloud KMSのHSMで署名処理を行う構成を取ることができる。この方式では、署名鍵の保管代金が2.50USD/月、署名処理は10,000署名当たり0.15USDと、コスト面で非常に有利である。
(いずれも2025年7月時点の価格)
このように署名コストをほとんど気にする必要がないため、本番・検証環境のどちらのバイナリにも気兼ねなく署名できる。その結果、どちらの環境でもバイナリの出自や改ざんされていないこと保証でき、より安全な配布環境を構築できる。また、署名コストを気にしなくて済むため、トラブル発生時にも再署名や検証などの対応を柔軟に行うことが可能である。
さらに、証明書発行に当たってに必要となる企業の存在確認において、Sectigo社とは日本語でやり取りできた点も大きな利点であった。
なお、"Windowsアプリのインストーラーのコード署名にどのベンダーが使われてるか調べてみた"で調べた限り、Sectigoのコード署名証明書はDigiCertに次いで多く使われており、発行体としての信頼性についても十分であると判断した。
環境構築の手順
Sectigoが手順をPDFで公開しているのでほぼそれに従った。
Google Cloud KMSのHSM上で署名用秘密鍵を生成する
コード署名に使用する秘密鍵は、Google Cloud KMSのHSM(Hardware Security Module)上で生成する。
この鍵は、鍵の安全性が損なわれない限り再生成の必要はなく、証明書の更新時にも同じ鍵を継続して使用できる(証明書の次回更新時はこのステップをスキップして構わない)
(1)キーリングの作成
鍵を束ねるためのキーリングを作成する。
リージョンはマルチリージョンasia1
を選択した。
(2) 署名用鍵を作成
次に署名に使用する鍵を作成する。
鍵名と保護レベルを指定する画面では、保護レベルとして 「HSM」 を選択する必要がある。
これは、コード署名証明書の発行要件において「秘密鍵はHSM内で生成され、HSM外に取り出せないこと」が求められているためである[1]。
目的とアルゴリズムでは、それぞれ『非対称な署名』と『4096 ビット RSA - PKCS#1 v1.5 パディング - SHA256 ダイジェスト』を選ぶ。
※この鍵種別を選んだ理由は、実際に流通しているWindowsバイナリのコード署名でこの組み合わせが多く使用されているためである(詳細はページ下部の備考を参照)
設定を終えたら、鍵の生成を実行する。
(3) 生成した鍵のAttestationを取得する
Attestationとは、『HSM上で鍵が安全に生成され安全に保管されていること』を証明するためのデータ。Sectigoなどの証明書ベンダーはコード署名証明書発行に当たって鍵の健全性をAttestationを使って検証する必要があるため、Google Cloud KMSからAttestationをダウンロードしておく必要がある。
Attestationをダウンロードするには、まず生成した鍵を選択し、三点メニューから「証明書を検証」を押す。
次に、表示されたダイアログで『証明書バンドルをダウンロード』を押すとzipファイルがダウンロードされる。これを保存しておき、証明書購入時に送る。
ローカルPCでCSRを作成する
証明書の発行にあたっては、署名鍵に対応するCSR(Certificate Signing Request)を作成する必要がある。
この手順はLinuxやWSL環境で行うと行いやすい。
CSR作成環境の全体構成は以下の図の通りである。
OpenSSLのEngine API(プラグイン機構)[2]にPKCS#11プラグインを組み込み、PKCS#11プラグインがGoogleのPKCS#11 Libraryを通じてGoogle Cloud HSMにアクセスし、秘密鍵を用いてCSRを生成する構成となっている。
必要なツールをインストール
まずは、OpenSSLからPKCS#11インターフェイスを通じてGoogle Cloud KMSにアクセスするためのツールをインストールする。
sudo apt-get install libengine-pkcs11-openssl opensc
次に、GoogleのPKCS#11ライブラリをGitHubのリリースページから取得し、任意のディレクトリに展開する。
pkcs#11 · Releases · GoogleCloudPlatform/kms-integrations
展開したディレクトリにある libkmsp11.so
のパスを、環境変数 PKCS11_MODULE_PATH
に設定する。
PKCS11_MODULE_PATH="/path/to/libkmsp11.so"
次に、CSR作成に利用する鍵が入ったキーリングのパスを示すYAMLを任意の場所に作成する。
YAMLの中身は次のようなもの。
YOUR_GCP_PROJECT_ID
やKEY_RING_NAME
は自身のGoogle CloudプロジェクトIDやキーリング名に置き換えること。
---
tokens:
- key_ring: "projects/YOUR_GCP_PROJECT_ID/locations/asia1/keyRings/KEY_RING_NAME"
作成したYAMLファイルのパスを、環境変数 KMS_PKCS11_CONFIG
に設定する。
KMS_PKCS11_CONFIG="/path/to/google_hsm_config.yaml"
次に、Google Cloud CLIでアプリケーションのデフォルト認証情報(ADC)の資格情報を取得する。ここでログインに使用するアカウントは、作成した鍵にアクセスできる権限を持っている必要がある。
gcloud auth application-default login
ADCが取得でき、上述のプラグインやYAMLでの設定が上手くいっていれば、次のコマンドで作成した鍵の情報が取得できるようになっている。
pkcs11-tool --module /path/to/libkmsp11.so --list-objects
最後にopensslコマンドでCSRを生成する。以下はCSR作成用のコマンド例である。
「EXAMPLE, Inc.」の部分は自社の英語名など適切なものに書き換え、またtest-authenticode-key
は作成した鍵の名前に置き換えて実行のこと。
openssl req -new -subj '/CN=EXAMPLE, Inc./' -sha256 -engine pkcs11 -keyform engine -key pkcs11:object=test-authenticode-key
コード署名証明書を購入する
AttestationとCSRの準備が整ったら、コード署名証明書の購入に進む。
Sectigo Code Signing Certificates での購入の場合、Certificate Delivery Methodには「Install on Existing HSM」を選ぶこと。
Validation OptionはExtended Validationを選ぶ意味はあまりない(ページ下部備考参照)ので、Standard Validationでよい。
購入時にAttestationとCSRを送信するように求められるので、適宜送信する。
購入後、証明書を発行するに当たっての審査がある。
法人名義での証明書発行を行う場合、以下のような手順が必要である:
- 申請者の本人確認
- パスポート自体の写真提出
- パスポートを顔の横に掲げた状態のセルフィー提出
- 会社の存在確認
- 法人番号を伝えると、Sectigo側で法務局の登記情報を照会して確認する
- 会社の電話番号確認
- 購入時に設定した会社の電話番号宛に自動音声電話がかかってきて数値を伝えられるので、それをWebフォームに入力して完了
上記のやり取りはSectigoの審査担当者と日本語で行えたためスムーズに行えた。
以上のような審査が終わると証明書が発行される。
GitHub Actionsに組み込む
証明書の発行が完了したら、GitHub Actions上のWindows Runnerにコード署名処理を組み込む。
署名処理には、Windows Runner上の signtool.exe を用いる。Google Cloud KMSに格納された署名用鍵は、Googleが提供する KMS CNG(Cryptography Next Generation)Provider を通じて利用する構成である。また、署名にはタイムスタンプを付与することができ、証明書の有効期限後も署名の有効性を保つことができる[3]。
以下に、署名処理を行うためのGitHub Actionsの構成例を示す。
- name: Sectigo BYO HSMでコード署名するために必要なGoogle Cloud KMS CNG providerをインストールする、SignTool.exeにパスを通す
run: |
curl -L https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/cng-v1.2/kmscng-1.2-windows-amd64.zip -o kmscng.zip
unzip kmscng.zip
cd kmscng-1.2-windows-amd64
msiexec /i kmscng.msi /quiet /qn /norestart
# 作業用ディレクトリを作成する
New-Item -Path "C:\Windows\KMSCNG" -ItemType Directory -Force
# SignTool.exeにパスを通す
echo "C:\Program Files (x86)\Windows Kits\10\App Certification Kit" >> $env:GITHUB_PATH
shell: pwsh
- name: Sectigoコード署名で使うBASE64された証明書をデコードしてファイルに保存する
run: |
[IO.File]::WriteAllBytes(
'C:\Windows\KMSCNG\cert.p12',
[Convert]::FromBase64String("${{ vars.sectigo_code_signing_cert_base64 }}")
)
shell: pwsh
# ここで使うアカウントには roles/cloudkms.signerVerifier を付与しておくこと
- name: Google Cloud KMS上のSectigoコード署名用秘密鍵にアクセスできるようにするためにGoogle Cloudへログインする
uses: google-github-actions/auth@v2
with:
project_id: YOUR_PROJECT_ID
create_credentials_file: true
workload_identity_provider: SAMPLE_PROVIDER
service_account: YOUR_SERVICE_ACCOUNT@PROJECT.iam.gserviceaccount.com
export_environment_variables: true
access_token_lifetime: 600
- name: コード署名する
run: |
signtool.exe sign /v /debug /as /fd sha256 /tr http://timestamp.sectigo.com /td SHA384 /f C:\Windows\KMSCNG\cert.p12 /csp "Google Cloud KMS Provider" /kc projects/YOUR_GCP_PROJECT_ID/locations/asia1/keyRings/KEY_RING_NAME/cryptoKeys/KEY_NAME/cryptoKeyVersions/1 C:\Temp\binary-to-be-signed.exe
shell: cmd
備考
コード署名で一般的に使用されている鍵種別とビット数について
実際のWindowsアプリケーションで、どのような鍵種別およびビット数がコード署名に用いられているかを調査した。2025年5月時点で手元にあった30個のWindowsアプリのインストーラーを対象に調べた結果は以下の通りである。
鍵・ビット数 | 個数 |
---|---|
RSA 4096bit | 17個 |
RSA 3072bit | 9個 |
ECC 384bit | 1個 |
ECC 256bit | 1個 |
この結果から、RSA 4096ビットを選んでおけば安牌ということが分かる。
EV証明書によるSmartScreenの即時回避は現在不可
かつては、EV(Extended Validation)コード署名証明書を用いてコード署名を行えば、Microsoft SmartScreenによる警告画面を回避することが可能であった。
しかし、2024年3月のWindowsの仕様変更により、EV証明書であってもSmartScreenによる警告を即時にバイパスすることはできなくなった。現時点では、証明書そのものや署名済みバイナリのレピュテーションが十分に蓄積されるまでは、SmartScreenの警告が表示される可能性がある。
その点については、SectigoのEVコード署名書購入ページにも次のように記載されている。
Note: In March 2024, Microsoft changed the way MS SmartScreen interacts with EV Code Signing certificates. EV Code Signing certificates remain the highest trust certificates available, but they no longer instantly remove SmartScreen warnings.
したがって、SmartScreen対策のためにEV証明書を選択する意味はなくなっている。
-
これは、NVIDIAのコード署名用秘密鍵流出に代表されるように秘密鍵が流出してコード署名証明書が悪用される事案が多数起きたため、そのような業界要件になった。 ↩︎
-
OpenSSL Engine APIについては記事を書いている OpenSSLとクラウドHSMやYubiKeyを連携可能にするOpenSSL Engine APIとは ↩︎
-
信頼できる第三者のタイムスタンプが付与されていると署名実施日時が証明可能となり、署名実施時点で証明書が確かに失効していなかったことを第三者が検証できるようになる。タイムスタンプがない場合、コード署名を実施した日時は署名実施者の自己申告の値になるため、第三者は署名実施時点で証明書が有効だったかどうかを検証できない(署名実施するマシンの日時を過去日時にして署名するということができてしまう)。そのため、証明書の有効期限が切れると、コード署名も無効と見なされる。 ↩︎
Discussion