electronの自動アップデート コードサイニングの仕組み (Windows版)


はじめに

初めてelectronでデスクトップアプリを作成し、色々壁に当たりながらも全て解消しリリースにこぎつけた。
今回は、リリース後初めての証明書の更新(Windows版)で生じた問題について原因と対応内容をまとめた。

背景

Web版やSPアプリで同機能のシステムをリリースしていて、対応チャネルを増やす目標があった。
Web版はreact/javascriptを使用していて、コンポーネントやロジックの共有も考え、
react + electron + javascriptを用いて1ソースでWindows/Macデスクトップアプリを作成した。

ある日...

リリースから10か月程経ちシステムは安定稼働している。
electronのコードサイニング証明書の期限(1年)が近づいてきたため、証明書を取得した。
今回は当初の発行元機関が証明書の発行業務をやめたため、新しい発行機関にて証明書を取得した。
コードサイニングはelectron-builderにて実装。
新しい証明書にて問題なくビルドでき、いざ自動アップデートの検証をして見る。
自動アップデートに失敗。むむむ。
なぜだ。

コードサイニングについて

コードサイニング証明書はソフトウェアの正当性を担保するもので、作成元で生成されたことを保証する。
1. コード・暗号化されたハッシュ値(デジタル署名)・コードサイニング証明書を使ってパッケージを作成
2. コードサイニング証明書の有効有無確認
3. コードサイニング証明書の公開鍵を取得
4. 暗号化されたハッシュ値(デジタル署名)を復号し証明書に含まれているハッシュ値を比較
ハッシュ値の一致により改ざんによる不正がないパッケージであることが証明される

electron-updaterについて

electronデスクトップアプリの自動アップデートにelectron-updaterを用いた。
electron-builderにelectron-updaterの組み合わせは参考文献も多く自然とその組み合わせを選択した。
electron-updaterは、インストール済みのパッケージ起動時に指定の配置先のパッケージのバージョンをチェックする。
配置先のバージョンがインストールより新しい場合、ローカルのtempフォルダ内に最新版をダウンロードし証明書をチェックする。
証明書チェックOKであれば、tempフォルダにダウンロードしたパッケージをインストールする。
証明書チェックがNGであれば、tempフォルダを削除しインストール失敗の結果を戻す。

自動アップデートに失敗した原因

ログをたどっていくと、ローカルtmpフォルダ内に最新版をダウンロードまでは行われていて、証明書チェックでNGとなっていることが分かった。証明書を含んだビルドも成功うしているため、証明書チェックでエラーとなる理由が分からない。
しばらく考えてみたが、原因が分からないため、ローカルにプルしているnpmモジュールを修正し原因になりそうな箇所にログを出して調査を進めてみた。
electron-updaterのverifySignature内で、インストール済みのパッケージのpublishNameと、証明書から取得した文字列をし署名が正しいかを判定している。
今回証明書発行元が変更になった際、発行者の名前を半角英数字とカナ文字で変更していた。

verifySignatureの処理内で以下の処理が行われるが、カナ文字の変換では正しく変換されない。
const data = parseOut(Buffer.from(stdout, "base64").toString("utf-8"))
これが原因であった。
カナ文字の発行者だった場合に証明書チェックが正しく行われず証明書チェックエラーとなる可能性がある。
今回はWindow版での処理について記載したが、Mac版は同じライブラリで別処理を通っている。

まとめ

自動アップデートのライブラリは外部ライブラリを使うことが多いと思うが、設計段階でどういう処理を行っているかの事前調査が必要だと改めて感じた。
electron-updaterでの自動アップデートでの失敗文献が少なかったため、似たような問題に当たった方の参考になればと思う。