🧑‍💻

#151 ElectronStoreの導入手順と暗号化アプローチについて

に公開

はじめに

今回はElectronで設定などを保存するのによく使われるElectronStoreの導入手順と設定をWindows環境で暗号化して保存する際のアプローチについて記載します。

ElectronStore:
https://github.com/sindresorhus/electron-store
https://www.npmjs.com/package/electron-store

導入手順

Electron Vite のセットアップ

前提として、Electronのセットアップは electron-vite を使用して行います。
セットアップは 公式に記載 のあるコマンドを実行すると、各設定について聞かれるので私は以下のようにセットアップしました。

✔ Project name: electron-vite
✔ Select a framework: react
✔ Add TypeScript: Yes
✔ Add Electron updater plugin: No
✔ Enable Electron download mirror proxy: No

セットアップが完了すると、 Project name で入力したディレクトリ内にテンプレートが生成されるので、 Project name ディレクトリに移動し、 npm i を実行しパッケージをインストールします。
その後、 npm run dev でelectronアプリが立ち上がることが確認できればElectronのセットアップは完了です。

electron-store のセットアップ

本題の electron-store をインストールしていきますが、electron-store のver9以降で ESM に移行した関係上、インストール後に ES Module に関する設定を行わなければimportに関するエラーなどが出てきてしまうためそれらも解決していきます。

※ 以下Issueでも議論されているようです。
https://github.com/sindresorhus/electron-store/issues/276


◆ electron-store のインストール
まずは以下のコマンドで electron-store をプロジェクトにインストールします。

npm install electron-store

インストールが完了後、electron-vite で生成されたテンプレート内にある src/main/index.ts を開き、ファイル内の52行目付近の以下のコードを編集します。


※ 編集前

index.ts
app.whenReady().then(() => {
  ~~ 前略 ~~
  // IPC test
  ipcMain.on('ping', () => console.log('pong'))
  ~~ 後略 ~~
})


※ 編集後

index.ts
import Store from 'electron-store'

app.whenReady().then(() => {
  ~~ 前略 ~~
  // IPC test
  ipcMain.on('ping', async () => {
    const store = new Store()
    store.set('sampleKey', 'sampleValue')
    console.log(store.get('sampleKey'))
  })
  ~~ 後略 ~~
})

上記のように記述することで、electron-store が生成する設定ファイルに { 'sampleKey': 'sampleValue' } という形で値を書き込み、書き込んだ設定を読み込み console に表示するということが可能です。

しかし、書き換え後のコードでは恐らくエディタ上の store.set や store.get の部分で プロパティ 'set' は型 'ElectronStore<Record<string, unknown>>' に存在しません。 というようなエラーが出るかと思います。


◆ ES Module への対応
electron-store がver9以降で ESM に移行した関係上、main プロセス内の設定も変えていく必要があります。

まずは package.json に以下の設定を追加します。

package.json
{
  ~~ 前略 ~~
  "type": "module",
  ~~ 後略 ~~
}

その後、 tsconfig.node.json にも以下の設定を追加します。

tsconfig.node.json
{
  ~~ 前略 ~~
  "compilerOptions": {
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
  }
  ~~ 後略 ~~
}

最後に、 src/main/index.ts 内の15行目付近のコードを編集します。

index.ts
function createWindow(): void {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 900,
    height: 670,
    show: false,
    autoHideMenuBar: true,
    ...(process.platform === 'linux' ? { icon } : {}),
    webPreferences: {
-     preload: join(__dirname, '../preload/index.js'),
+     preload: join(__dirname, '../preload/index.mjs'), // 拡張子を .js → .mjs に変更
      sandbox: false
    }
  })
  ~~ 後略 ~~
}

このように設定することで、先ほどまでエラーが出ていた store.set や store.get の呼び出し箇所を見てみるとエラーがなくなっていることが確認できるかと思います。
この状態で npm run dev を実行しアプリを立ち上げ、アプリ内に表示されている Send IPC というボタンをクリックすると、console 上に sampleValue という出力が確認でき、正常に設定の保存と読み込みが行えていることがわかります。

Windows環境での設定暗号化に関するアプローチ(おまけ)

electron-store では設定の暗号化と複合化に関する機能が提供されています。
実際にその機能を使用し、Windows環境でどのように暗号化と複合化の鍵を管理するかについて1つのアプローチを記載します。


◆ アプローチ
今回、鍵の管理としてWindows資格情報に鍵を保存し利用することにします。
その際に利用するライブラリとして以下をインストールしておきます。

npm install keytar

その上で src/main/index.ts 内の先ほど electron-store の設定値を書き込み、呼び出ししていた箇所を編集します。

index.ts
import Store from 'electron-store'
import keytar from 'keytar'
import { randomBytes } from 'crypto'

const SERVICE_NAME = 'testElectronApp'
const ACCOUNT_NAME = 'EncryptionKey'

async function getEncryptionKey(): Promise<string> {
  // Windows Credential Manager から鍵を取得
  let key = await keytar.getPassword(SERVICE_NAME, ACCOUNT_NAME)

  // 見つからなければ、新しい鍵を生成し保存
  if (!key) {
    key = randomBytes(32).toString('hex') // 256bitの鍵
    await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, key)
  }

  return key
}

  ~~ 前略 ~~

app.whenReady().then(() => {
  ~~ 中略 ~~
  // IPC test
  ipcMain.on('ping', async () => {
    const encryptionKey = await getEncryptionKey()
    console.log('encryptionKey => ' + encryptionKey.slice(0, 2) + '*****' + encryptionKey.slice(-2))
    const store = new Store({ encryptionKey })
    store.set('secret', 'SecretValue')
    console.log('DecryptionValue => ' + store.get('secret'))
  })
  ~~ 後略 ~~
})

このように書くことで、

  1. keytarを利用し資格情報を確認し鍵を取得
  2. まだ鍵がなければ作成
  3. electron-store では 1と2 で取得した鍵を使って暗号化と複合化を行う

という手順で設定を暗号化することができます。

実際に先ほどと同じ手順でアプリを立ち上げ Send IPC ボタンをクリックすると、

encryptionKey => 56*****2b
DecryptionValue => SecretValue

このように出力され、鍵の取得と複合化されたValueの値の取得が正常に行えていることが確認できます。

また、Windowsの「資格情報マネージャー」から「Windows資格情報」に遷移すると下記画像のようにアプリ側で設定した SERVICE_NAME などの値で情報が追加されていることが確認できます。

key_info.jpg

おわりに

今回は個人開発で手間取った electron-vite の ESM への対応手順、設定ファイルの暗号化や複合化に使用する鍵の管理方法に関するアプローチについて、調べてもあまり情報がなかったので書いてみることにしました。
何かのお役に立てれば幸いです。ここまで読んでいただきありがとうございました。

Discussion