GPG の使い方をおおまかに理解しよう

GPG を使ってみよう！ということで、GPG を使って Git のコミットに署名をしてみました。
なので自分用に手順の記録がてら、おおまかに解説をしてゆきます。
(おおまかに分かったら公式や他の記事を見ることをおすすめします)

また今回は GPG を個別でインストールせずに、 Git Bash に付いてる GPG を使います。
Windows でちょっとした Linux っぽいことをしたいときは WSL も便利ですが Git Bash も便利ですね。
なので今回は Git Bash でコマンドを実行します。

## 環境

Windows 11
Git 2.45.2.windows.1

## やるまえにそもそも GPG ってなに

GPG は公開鍵と秘密鍵のキーペアを作れたり、
作ったキーペアを使って証明・署名・暗号化・認証などができる便利なやつです。

いろいろ機能がありますが、実際に使うときのイメージはこんな感じです。

- `gpg --full-generate-key`とかでキーペアをつくれます。つくった鍵は GPG がいい感じに持ってます
- `gpg --list-keys`とか`gpg --list-secret-keys`で鍵の一覧が見られます
  - `--keyid-format=long`とオプションをつけると鍵の ID が見られます
- `gpg --encrypt {ファイル}`とか`gpg --decrypt {ファイル}`で暗号化とか復号ができます
- `gpg --armor --export {鍵のID}`とか`gpg --armor --export-secret-key {鍵のID}`で GPG が持ってる鍵をエクスポートできます

また SSH とかの単純なキーペアと違って、マスターキーとサブキーというキーペアの親子みたいな概念があります。
マスターキーがあればサブキーを追加で作ったりもできます。
（暗号化用のサブキーを作る、署名用のサブキーを作る、など）
マスターキーとサブキーどちらでも暗号化や署名ができますが、
マスターキーには強い権限があるので権限の弱いサブキーを使った方が漏洩したときに被害がすくなかったりしてうれしいですね。

## やること

1. GPG でキーペアを作成する
2. 作成した秘密鍵でコミットに署名するよう Git を設定する

## GPG で署名用のキーペアを作成する

まずは`gpg --full-generate-key`でキーペアを作ります。
コマンドを実行すると対話的にどんなキーペアを作るかを聞かれます。
今回はほぼデフォルトを使用しています。

```shell-session
$ gpg --full-generate-key
gpg (GnuPG) 2.4.5-unknown; Copyright (C) 2024 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (9) ECC (sign and encrypt) *default*
  (10) ECC (sign only)
  (14) Existing key from card
Your selection?
★暗号アルゴリズムやどんな鍵を作るか。デフォルト(ECCで署名と暗号化)を選択しました。

Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (4) NIST P-384
   (6) Brainpool P-256
Your selection?
★暗号化に使う楕円曲線に何を使うか。デフォルト(Curve 25519)を選択しました。

Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
★鍵の有効期限をいつまでにするか。今回は無期限にしました。

Key does not expire at all
Is this correct? (y/N) y
★入力確認。

GnuPG needs to construct a user ID to identify your key.
Real name: maze
Email address: maze@example.com
Comment: good key!
You selected this USER-ID:
    "maze (good key!) <maze@example.com>"
★鍵の所有者の名前とかコメント。今回はダミーを設定していますが、
★名前やメールアドレスは所有者の検証に使われたりするので気にする必要があることが多いです。

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
★入力確認。

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
★このあたりでパスフレーズを設定するかどうか聞かれたりします。
★パスフレーズが設定してあると秘密鍵を使うためにパスフレーズの入力が必要になるので、
★秘密鍵の漏洩などに対してすこし安全になります。なので設定しておくほうがよいです。

gpg: revocation certificate stored as '/c/Users/maze/.gnupg/openpgp-revocs.d/80C30BCBE9986D342EF7F62E2F686A321F6A25EA.rev'
public and secret key created and signed.

pub   ed25519 2024-11-15 [SC]
      80C30BCBE9986D342EF7F62E2F686A321F6A25EA
uid                      maze (good key!) <maze@example.com>
sub   cv25519 2024-11-15 [E]
★できあがった鍵の内容。`gpg --list-keys`で後から同じやつを表示できます。
```

これでキーペアができました。
公開鍵のリスト表示`gpg -K`と秘密鍵のリスト表示`gpg -K`に`--keyid-format=long`オプションを付けて確認してみます。

```shell-session
$ gpg -k --keyid-format=long
[keyboxd]
---------
pub   ed25519/2F686A321F6A25EA 2024-11-15 [SC]
      80C30BCBE9986D342EF7F62E2F686A321F6A25EA
uid                 [ultimate] maze (good key!) <maze@example.com>
sub   cv25519/56FBB24D955E1BD6 2024-11-15 [E]
★pub -- マスターキーの公開鍵(public)。
         `2F686A321F6A25EA`がIDです。
         [SC]なので署名(sign)と証明(certify)ができます。
★uid -- 鍵の所有者の名前とかコメント。
★sub -- サブキーの公開鍵(public sub)。
         `56FBB24D955E1BD6`がIDです。
         [E]なので暗号化(encrypt)ができます。
```

```shell-session
$ gpg -K --keyid-format=long
[keyboxd]
---------
sec   ed25519/2F686A321F6A25EA 2024-11-15 [SC]
      80C30BCBE9986D342EF7F62E2F686A321F6A25EA
uid                 [ultimate] maze (good key!) <maze@example.com>
ssb   cv25519/56FBB24D955E1BD6 2024-11-15 [E]
★sec -- マスターキーの秘密鍵(secret)。
         `2F686A321F6A25EA`がIDです。
         [SC]なので署名(sign)と証明(certify)ができます。
★uid -- 鍵の所有者の名前とかコメント。
★ssb -- サブキーの秘密鍵(secret sub)。
         `56FBB24D955E1BD6`がIDです。
         [E]なので暗号化(encrypt)ができます。
```

GPG では鍵の種類がこんな感じで表記されています。

|             | マスターキー(略称無し) | サブキー(sub) |
| ----------- | ---------------------- | ------------- |
| 公開鍵(pub) | pub                    | sub           |
| 秘密鍵(sec) | sec                    | ssb           |

サブキーの略し方が、秘密鍵は`s(ecret)s(u)b`なのに公開鍵は`(public)sub`なのでちょっとこんがらがりやすいですね。注意しましょう。

というわけでマスターキーを使えば署名できる状態になりましたが、前述したようにマスターキーを使うよりサブキーを使う方が安全でうれしいです。
なので次に署名用のサブキーをつくっていきます。

`gpg --edit-key {鍵のID}`で対話的に鍵の編集ができます。

```shell-session
$ gpg --edit-key 2F686A321F6A25EA
gpg (GnuPG) 2.4.5-unknown; Copyright (C) 2024 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  ed25519/2F686A321F6A25EA
     created: 2024-11-15  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/56FBB24D955E1BD6
     created: 2024-11-15  expires: never       usage: E
[ultimate] (1). maze (good key!) <maze@example.com>

gpg> addkey
★サブキーを追加するコマンド。

Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
  (10) ECC (sign only)
  (12) ECC (encrypt only)
  (14) Existing key from card
Your selection? 10
★署名だけできるサブキーを追加したいので10を選択しました。

Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (4) NIST P-384
   (6) Brainpool P-256
Your selection?
★暗号化に使う楕円曲線に何を使うか。デフォルト(Curve 25519)を選択しました。

Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
★鍵の有効期限をいつまでにするか。無期限にしました。

Key does not expire at all
Is this correct? (y/N) y
★入力確認。

Really create? (y/N) y
★入力確認２（ツー）。

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

sec  ed25519/2F686A321F6A25EA
     created: 2024-11-15  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/56FBB24D955E1BD6
     created: 2024-11-15  expires: never       usage: E
ssb  ed25519/533AD80C010DDE04
     created: 2024-11-15  expires: never       usage: S
[ultimate] (1). maze (good key!) <maze@example.com>

gpg> q
★編集を終了するコマンド。

Save changes? (y/N) y
★保存して終了しました。
```

これで署名用のサブキーが追加できました。
確認してみましょう。

```shell-session
$ gpg -k --keyid-format=long
[keyboxd]
---------
pub   ed25519/2F686A321F6A25EA 2024-11-15 [SC]
      80C30BCBE9986D342EF7F62E2F686A321F6A25EA
uid                 [ultimate] maze (good key!) <maze@example.com>
sub   cv25519/56FBB24D955E1BD6 2024-11-15 [E]
sub   ed25519/533AD80C010DDE04 2024-11-15 [S]

```

ID が`533AD80C010DDE04`の署名用のサブキーが増えています。いい感じですね。
これでキーペアの作成は完了です。

## 作成した秘密鍵でコミットに署名するよう Git を設定する

今度は作った鍵を使って Git のコミットに署名をしてみます。
Git のコンフィグに「GPG のこの鍵を使って署名してね」と設定すれば署名してくれます。かんたん！

```shell-session
$ git config --global user.signingkey 533AD80C010DDE04
★gitの署名用のキーとして、作った署名用のサブキーのIDを設定しました。

$ git config --global commit.gpgsign true
★コミットのGPG署名を有効にしました。
```

これでコミットしたときに自動的に秘密鍵を使って署名してくれます。

### (おまけ) GitHubなどのGitホスティングサービスと連携する

コミットに署名をできたら、
GitHubなどでアカウントと署名を紐づけてコミットに確認済みマークを付けたいですね！

`gpg --armor --export {鍵のID}`でGPGから公開鍵をエクスポートできます。

```shell-session
$ gpg --armor --export 3AA5C34371567BD2
★ここにテキスト表現された公開鍵の表示されます。
```

表示された公開鍵をGitHubなどの設定画面で自分のアカウントに登録することでアカウントと署名が紐づいて確認マークが付きます。

注意点として、GitHubの場合は鍵の所有者のメールアドレスとGitHubに登録しているメールアドレスが一致している必要があります。(おそらく大抵のサービスはそう)

### (おまけ) 秘密鍵のパスフレーズ入力について

秘密鍵にパスフレーズを設定している場合はコミットのときにパスフレーズの入力が必要です。
GPG の gpg-agent にパスワード入力をキャッシュする機能があり、デフォルトだと 300 秒(5 分)ほど TTL があるのでその間はパスフレーズを入力しなくてよいです。
また、キャッシュを使うたびに TTL が延長されます(上限あり)。

TTL を長くしたい場合は`{ユーザーフォルダ}/.gnupg/gpg-agent.conf`を作って設定するとよいです。

```conf:gpg-agent.conf
default-cache-ttl 7200
max-cache-ttl 28800
```

`default-cache-ttl`でキャッシュの TTL を指定でき、
`max-cache-ttl`で TTL を延長する上限を指定できます。