IntelliJ Platform Plugin開発にて、Plugin Signing を試してみた

IntelliJ Platform Plugin SDKのドキュメントを読んでいたところ、 Plugin Signing というページがありました。
Plugin Signing | IntelliJ Platform Plugin SDK

そこで、自作のプラグイン Railroads にPlugin Signingしてみたときのメモを残します。

 
目次

 

環境

 

署名の実施

ドキュメントに従い、署名を実施してみます。

 

秘密鍵の生成

ドキュメントの Generate Private Key に従い、秘密鍵を生成します。
https://plugins.jetbrains.com/docs/intellij/plugin-signing.html#generate-private-key

WSL2を開き、以下を実行します。

今回 pem ファイルは railroads_private_encrypted.pem とします。

また、パスワードも設定しておきます。

# 実行
$ openssl genpkey\
  -aes-256-cbc\
  -algorithm RSA\
  -out railroads_private_encrypted.pem\
  -pkeyopt rsa_keygen_bits:4096

# パスワード入力
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

 
続いて、生成した鍵をRSA形式に変換します。

# 実行
$ openssl rsa\
  -in railroads_private_encrypted.pem\
  -out railroads_private.pem

# パスワードを求められるので、railroads_private_encrypted.pem を生成したときのパスワードを入力
Enter pass phrase for railroads_private_encrypted.pem:

# 結果
writing RSA key

 
最後に、 crt ファイルを生成します。

# 実行
$ openssl req\
  -key railroads_private.pem\
  -new\
  -x509\
  -days 3650\
  -out railroads_2024_0406.crt

# 注意書きが表示された
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.

# 適当な値を入力
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:thinkAmi
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:https://github.com/thinkAmi
Email Address []:

 

IDE環境変数に各鍵の値を設定

プラグインへの署名については

  1. Gradle IntelliJ Plugin の signPlugin タスク
  2. Gradle IntelliJ Plugin の publishPlugin タスク
  3. CLI

のいずれかの方法で行えば良いようです。

今回は

ということで、上記1. の方法で進めることにします。

 
次に、 signPlugin タスクで実行するためには、

  1. build.gradle.ktsファイルにて、秘密鍵などの情報をハードコード
  2. build.gradle.ktsファイルにて、秘密鍵などのファイルを読み込むよう指定
  3. IntelliJ IDEAの環境変数に、秘密鍵などをBASE64化した値を設定

のいずれかが必要そうでした。

今回は

ということで、上記3.の方法を取ることにしました。

 
そこで、IntelliJ IDEAの環境変数に設定できるよう、

  • railroads_2024_0406.crt
  • railroads_private.pem

の2ファイルの中身をBASE64化することにします。

また、都度BASE64化するのは手間なので、

  • certificate ディレクトリを作り、その中にpemやcrtを置く
  • 誤ってコミットしないよう、 .gitignore にて、 *.pem*.crt を追加する
  • certificate ディレクトリに何を置くか忘れないよう、pemやcrtのサンプルファイルを追加する
  • certificate ディレクトリの中にある pem や crt の中身をBASE64化するRubyスクリプトを作成する

という作業を行います。

 
ちなみに、Rubyスクリプト (base64encode.rb) は以下のような感じです。

なお、BASE64化するときに改行を追加しないよう、 Base64#strict_encode64 を使っています。
https://docs.ruby-lang.org/ja/latest/class/Base64.html#M_STRICT_ENCODE64

require 'base64'

file_paths = Dir.glob('*')
file_paths.each do |file_path|
  file_name = File.basename(file_path)

  # Do not output Base64-encoded strings for running Ruby scripts and example files
  next if file_name == File.basename(__FILE__)
  next if File.extname(file_name) == '.example'

  file_data = File.read(file_path)

  # Do not add newlines when Base64 encoded
  encoded_data = Base64.strict_encode64(file_data)

  puts "File Name: #{file_name}"
  puts "Encoded Data:"
  puts encoded_data
  puts "\n\n\n"
end

 
できたRubyスクリプトを実行し、BASE64化した値を確認します。

>ruby base64encode.rb
File Name: railroads_2024_0406.crt
Encoded Data:
***

File Name: railroads_private.pem
Encoded Data:
***

File Name: railroads_private_encrypted.pem
Encoded Data:
***

 
準備ができたので、公式ドキュメントに従い、IntelliJ IDEAで signPlugin タスクを追加します。
https://plugins.jetbrains.com/docs/intellij/plugin-signing.html#provide-secrets-to-ide

 
まず、 Run > Edit configuration から、 +Gradle を選択します。

続いて、以下の設定を行います。

  • Name: (任意の値)
  • Run on: Local machine (デフォルト)
  • Run: singPlugin
  • Gradle project: railroads (デフォルト)
  • Environment variables は、以下の3つの値を設定
    • なお、公式ドキュメントとは異なり、今回は署名するだけなので PUBLISH_TOKEN は設定不要
Name Value
CERTIFICATE_CHAIN railroads_2024_0406.crt の中身をBase64エンコードしたもの(改行なし)
PRIVATE_KEY railroads_private.pem の中身をBase64エンコードしたもの(改行なし)
PRIVATE_KEY_PASSWORD OpenSSLで railroads_private_encrypted.pem を作成したときに設定したパスワード

 
設定ができたので、 railroads [signPlugin] を実行します。

すると、以下のログが出力されました。成功したようです。

...
> Task :signPlugin

BUILD SUCCESSFUL in 59s
17 actionable tasks: 9 executed, 8 up-to-date
Configuration cache entry stored.
11:43:58: Execution finished 'signPlugin'.

 
また、 build/distributions/railroads-0.1.1-signed.zip というファイルも生成されていました。

 

署名の検証

では、次にプラグインの署名を検証します。

検証方法としては

  • Gradle で verifyPluginSignature タスクの実行
  • CLIで検証

の2つがありました。

Gradleのタスクで検証するためにドキュメントを読んだところ、環境変数から値を読み込んだり、動的に対象のプラグインファイルを指定するのが難しそうでした。
https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#tasks-verifypluginsignature

そこで今回はCLIで検証することにしました。

 
ドキュメントによると、CLIで検証するには CLI Tool が必要そうでした。
https://plugins.jetbrains.com/docs/intellij/plugin-signing.html#cli-tool

そこで、ドキュメントに従い、 marketplace-zip-signer-cli.jar をダウンロードしました。

 
続いて、署名をしたプラグインに対してツールを実行してみましたが、何も出力されませんでした。

> C:\Users\<UserName>\.jdks\jbr-17.0.9\bin\java.exe -jar marketplace-zip-signer-cli.jar verify -in "build\distributions\railroads-0.1.1-signed.zip" -cert "certificate\railroads_2024_0406.crt"

 
次に、署名がないプラグインに対してツールを実行したところ、メッセージ Provided zip archive is not signed が表示されました。

> C:\Users\<UserName>\.jdks\jbr-17.0.9\bin\java.exe -jar marketplace-zip-signer-cli.jar verify -in "build\distributions\railroads-0.1.1.zip" -cert "certificate\railroads_2024_0406.crt"

Provided zip archive is not signed

 
念のためソースコードを見たところ、成功したときは何も出力されないのが正解のようでした。
https://github.com/JetBrains/marketplace-zip-signer/blob/1b365366563540d8b70f46578cc10c3bcd541b13/cli/src/main/kotlin/org/jetbrains/zip/signer/ZipSigningTool.kt#L90

 

その他資料:Busy Plugin Developers

JetBrainsがYoutubeで公開している Busy Plugin Developers #1 の21分あたりから、Pluginについてふれられていました。
Plugin Signing at JetBrains Marketplace. IntelliJ Plugins UI Testing - YouTube

 

ソースコード

Githubに追加しています。
https://github.com/thinkAmi/railroads

今回のプルリクはこちら。
https://github.com/thinkAmi/railroads/pull/8