GPG に入門した話

この記事は [慶應義塾大学 SFC 村井研 (RG) Advent Calendar 2021](https://adventar.org/calendars/6512) 12 日目 kino-ma の記事です

---

## 概要
この前、 GPG に入門して使い始めました。筆者より詳しい先輩方がたくさんいらっしゃるので恐縮ですが、まだ使っていない人が興味を持つ機会になればいいかと考えて今回この記事を書くことにしました。

この記事に書いてあることは、以下の通りです。

- GPG とはなんなのか
- GPG を使うと何が良いのか
- GPG に入門するにあたり、筆者が行なった手順

筆者が入門してから一ヶ月ほどが経過しており、記憶が曖昧です。また、この記事では形式上断言調を多用しますが、全て筆者の理解です。したがって誤りがたくさんあると考えられますので、指摘していただけると幸いです。

## GPG とはなんなのか
[GPG](https://gnupg.org/) (GNU Privacy Guard) とは、 OpenPGP という規格の一つの実装です [^one-of-implementation]。そして [OpenPGP](https://www.openpgp.org/) とは:

>OpenPGP is the most widely used email encryption standard. It is defined by the OpenPGP Working Group of the Internet Engineering Task Force (IETF) as a Proposed Standard in RFC 4880. OpenPGP was originally derived from the PGP software, created by Phil Zimmermann.

で、つまりメール暗号化の標準規格です。 [RFC 4880](https://www.ietf.org/rfc/rfc4880.txt) で定義されています。 GPG は OpenPGP の有名な実装として広く使われており、 GitHub 等のプラットフォームで使うことができます。

OpenPGP では公開鍵・秘密鍵のペアを作成し、その鍵ペアを使ってさまざまなことを実現できます。たとえば OpenPGP は暗号化以外にデジタル署名の機能を持っており [^rfc-features]、 Git では GPG の署名機能を使ってコミットを署名することができます。

OpenPGP の秘密鍵には二種類があり、それぞれ **主鍵** と **副鍵** です。主鍵は名前の通り主たる鍵で、一つだけ存在します。主鍵からいくつでも副鍵をつくることができ、それぞれの副鍵の持つ機能を制限して使い分けることもできます。また各秘密鍵は、ホストコンピュータに保管するだけでなく、 Yubikey などのセキュリティキーに格納することができます。 Yubikey に一度書き込んだ秘密鍵は読み出すことができず、よりセキュアであると考えられます。

秘密鍵の対になる公開鍵は世界中に公開し、誰でも見えるようにします。第三者がデータを暗号化したり署名を検証する際には、この公開鍵を使います。

イメージや役割としては SSH の鍵ペアと同じですが、 GPG の主鍵は個人と強く結びつくので、より機密性が高いと考えられます。

[^one-of-implementation]: 関係性としては、 HTTP というプロトコル（規格）に対してクライアントおよびサーバの実装が様々あるようなイメージです。
[^rfc-features]: RFC を見ると、ほかに compression、Radix-64 conversion の機能があるようです。二つ目についてはよくわかっていません。

## GPG を使うと何が良いのか
なぜ GPG を使うのかという話です。しばらく使ってみて筆者が個人的に感じたメリットを挙げます。

### GitHub のコミット履歴に :white_check_mark: Verified がつく
筆者が GPG に入門したのは、実はこれが主な理由です。

なんとなく先輩の GitHub リポジトリを眺めていたら、コミット履歴にズラッと「:white_check_mark: Verified」が並んでおり、かっこよかったのが動機になりました。

![Screen Shot 2021-12-11 at 23.30.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508344/17e9d89a-d9ac-343d-491a-b251fc28cbe7.png)

このマークは、そのコミットの作成元がアカウント [^associated-key] の正当な所有者であることを示しています。以前から見かけることはありましたが、とても複雑な手順が必要なのだろうと足踏みしており、なかなか手を出せずにいました。しかしたまたま時間があったので入門したところ、そうでもありませんでした。手順については後述します。

[^associated-key]: に紐づけられた鍵ペア

### SSH の鍵を統一できる
GPG は、暗号化・デジタル署名に加えて認証の機能を持っています。この機能では、 GPG Agent というソフトウェアが SSH Agent のように振る舞うことで、 OpenPGP 鍵を使って SSH の公開鍵認証を行うことができます。

これで何が嬉しいかというと、マシンごとに SSH 秘密鍵を生成し、それぞれリモートマシンに登録する必要がなくなります。そして鍵の管理をしやすくなるだけでなく、万が一秘密鍵が漏洩した場合に失効すべき公開鍵は一つだけで良くなるので、対応しやすくなることも考えられます。

### さまざまなサービスの二要素認証に使える
秘密鍵を Yubikey などのハードウェアキーに書き込んだ場合、パソコンの USB ポートなどに差し込むことで一部のサービスにおける認証の二要素目として利用することができます。筆者は、 Google や GitHub 等のサービスで設定しています。お気持ちですが、 SMS やメールよりも安心感があります（し、おそらく実際にハックされにくいでしょう）。

### なんとなくカッコいい
なんとなくカッコいいです。

### その他
おそらく専門家やもっと詳しい方だと、サイバー空間におけるアイデンティティを証明できることについてさらなる嬉しさを感じていることでしょう。あるいはさらなる深遠な思想が存在することでしょう。しかし筆者は、今の所上記のような嬉しさが主になっています。

## GPG に入門するにあたり、筆者が行なった手順
そんな GPG に筆者が入門した際、設定するにあたってやったことです。

基本的には「[GPG で始める暗号・署名ライフ](https://blog.livewing.net/gpg-life)」を読んで作業していたので、より詳細な解説についてはそちらを参照してください。また、注意点等も詳細に書かれているため、実際にセットアップする際は一度目を通すことをお勧めします。

なお、筆者は今の所 mac OS でのみ設定をしており、 Linux や Windows については確認しておりません。上記のサイトを参照してください。

### GPG のインストール
```sh
brew install gnupg
```

```sh
$ gpg --version
gpg (GnuPG) 2.3.3
libgcrypt 1.9.4
Copyright (C) 2021 Free Software Foundation, Inc.
License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: /Users/kino-ma/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
AEAD: EAX, OCB
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2
```

### 鍵の生成、保管、削除
コマンド等は「[GPG で始める暗号・署名ライフ](https://blog.livewing.net/gpg-life)」と同じなので、省略します。大まかな手順としては、以下の通りです。

1. GPG のインストール
1. 主鍵の生成
    - 筆者は Certify （鍵に対する署名）のみが可能な ED25519 鍵を無期限で生成した
    - パスフレーズは推測されにくく長いものを設定した
1. 副鍵の生成
    - 筆者は署名、暗号化、認証の鍵をそれぞれ一つずつ生成した
    - 有効期限はとりあえず五年にした
1. 主鍵の保管・削除
    - (**注**) 次の手順で Yubikey にインストールする場合は、主鍵が必要になるのでそちらを先にやります。
    - インターネットに接続しない記憶媒体を新しく用意し、暗号化して格納した
    - 冗長性のため、二次元 QR コードをプリントアウトして安全な場所に保管した
        - `brew install qrencode`
        - `qrencode -o secret-qr.png < secret-key.asc`
    - 一時的にエクスポートした鍵データを安全に消すには、 `shred -u`
    - 一緒に失効証明書も消しておく

この手順を踏むと、主鍵はホストマシン上には存在しなくなり、代わりに役割ごとに一つ、計三つの副鍵だけを扱えるようになります。

ここで生成した秘密鍵は、 Yubikey 等を使わない場合基本的に全てのマシンで使い回します。オフラインストレージを経由するなど、セキュアな方法で移行します。これについても詳しくは「[GPG で始める暗号・署名ライフ](https://blog.livewing.net/gpg-life)」へお願いします。

#### 公開鍵を、公開！
インターネットに公開鍵を公開しましょう。専用の鍵サーバネットワークが存在したり、いまは [keys.openpgp.org](https://keys.openpgp.org/) がメジャーだったりするらしいです。

筆者は、とりあえず GitHub に登録したのと、自分のホームページ (www\.kino\.ma) 上で [配信](https://www.kino.ma/kino-ma.gpg) しています。そのうち Web ページ上に書いたり SNS に置いたりしたいですね。

KeyBase アカウントを持っている人は、ここで紐づけるのもいいでしょう。二つ目以降の公開鍵を公開するには、 `keybase select --multi` だったと思います [^select-error]。

[^select-error]: エラーが出る場合は→ https://github.com/keybase/keybase-issues/issues/4025#issuecomment-874497887

### YubiKey へのインストール
副鍵を全てホストマシンに置いていても普通に GPG を使えるのですが、それだと鍵の移送や管理など色々な手間が発生します。

副鍵を Yubikey 等のハードウェアキーにインストールすることで、セキュアかつ便利に GPG を使うことができます。

#### Yubikey の初期設定
Yubikey を新しく入手した場合、 PIN などが共通の初期設定になっているので変更する必要があります。

1. `gpg --edit-card`
1. `passwd` -> `1 - change PIN`
    - PIN を設定する
    - 初期設定は確か `123456`
1. `admin`
    - 管理者モードに入る
1. `passwd` -> `3 - change Admin PIN`
    - 管理者 PIN を変更する
    - 初期設定は確か `12345678`
1. `Q` で抜ける

ここで設定した {,管理者}PIN は、これからの手順でちょいちょい聞かれると思うので、その都度答えましょう。

#### Yubikey に焼く
初期設定がおわったので、生成した副鍵を Yubikey に焼いていきます。手順は [公式ページ](https://support.yubico.com/hc/en-us/articles/360013790259-Using-Your-YubiKey-with-OpenPGP) に書いてあります。主鍵を焼いている点など細かいところが違いますが、もし以下の通りに行かなかったら参照してください。

1. `gpg --edit-key 1234ABC  # 自分の鍵 ID` [^key-id]
1. 各鍵を一つずつ Yubikey に入れていく
    1. `[S]` がついた鍵＝署名鍵が（主鍵を除いて）上から何個目か見て、 `key ${N}` で選択する
    1. `keytocard`
    1. スロットを聞かれたら `1` を選択する
    1. 同様に暗号化鍵 `[E]` を `2`、認証鍵 `[A]` を `3` に入れる
1. `quit`

[^key-id]: 鍵 ID は `gpg --list-secret-keys --keyid-format long` で調べることができる

ここまでできたら、「主鍵の保管・削除」にもどってよいです。また、筆者はこの段階で副鍵もホストマシンから消し去りました。

**一度 Yubikey に焼いた鍵は二度と読み出せないので、バックアップ等注意深く作業を行いましょう**。

#### Yubikey の管理について
PIN による保護があるといっても、念の為できるだけ人に触れられないようにしましょう。筆者は、研究室に入るための ID や家の鍵と一緒に、キーチェーンで腰にぶら下げています。

伸び縮みするキーリールを買ったのですが、引っ張って手を離すと自動で巻き取られるので Yubikey を差すことに適していないです。おすすめのやり方があれば教えてください。

### 使ってみる
以上で、 GPG 鍵を使う準備は整いました。

色々な使い道は「[GPG で始める暗号・署名ライフ](https://blog.livewing.net/gpg-life)」に書いてあります。その中でも、筆者が使ってみたかった **Git コミットの署名** と、ちょっと詰まった **SSH 認証** について書きます。

Yubikey に鍵を焼いた場合、鍵を使うためには USB ポートなどに差し込んで作業を行います。

#### Git コミットの署名
簡単には、 `git commit` コマンドに `-S` オプションをつけると署名が行えます。その場合鍵は自動的に選択されます。

```sh
$ git init
Initialized empty Git repository in /path/to/project
$ echo hello > test.txt
$ git add test.txt
$ git commit -m 'unsigned commit'
[main (root-commit) 8fc5876] unsigned commit
 1 file changed, 1 insertion(+)
 create mode 100644 test.txt
$ sed 's/$/, updated!/' -i test.txt
$ cat test.txt
hello. updated!
$ git add test.txt
$ git commit -m 'signed commit' -S
[main 31d0e8d] signed commit
 1 file changed, 1 insertion(+), 1 deletion(-)
```

コミットの署名は、 `git log` の `--show-signature` オプションで検証・表示することができます。

```sh
$ git log --show-signature
commit 31d0e8da50a01d64eb373c5c8e4a2e55e79a536e (HEAD -> main)
gpg: Signature made 日 12/12 01:14:19 2021 JST
gpg:                using EDDSA key 5A4D45DC6A7C83A23E1D01B9F0D7A2F02CAE648A
gpg: Good signature from "kino-ma <ma@kino.ma>" [ultimate]
Author: kino-ma <ma@kino.ma>
Date:   Sun Dec 12 01:14:19 2021 +0900

    signed commit

commit 8fc587679016c0f0b4648276dcb43060063e0336
Author: kino-ma <ma@kino.ma>
Date:   Sun Dec 12 01:13:09 2021 +0900

    unsigned commit
```

また、 GitHub の Settings -> SSH and GPG keys から、アカウントに紐づく GPG 公開鍵を登録することができます。そして署名付きのコミットを push すると、ちゃんと「:white_check_mark: Verified」が出ます。やったー :tada:

![Screen Shot 2021-12-12 at 3.00.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508344/d49aca63-80e2-1366-2c72-f06f834d9420.png)


毎回のコミットで自動で署名を行うには、 Git のグローバルコンフィグで `commit.gpgsign` を `true` にします。 `user.signingkey` で、使う秘密鍵の指紋を指定することもできます。

```sh
$ git config --global commit.gpgsign true

# 指紋の表示
$ gpg -K --keyid-format=long
/Users/kino-ma/.gnupg/pubring.kbx
---------------------------------
sec#  ed25519/405B5317D0AAF726 2021-11-14 [C]
      A968EDFCE40DDF9016F28629405B5317D0AAF726
uid                 [ultimate] kino-ma <ma@kino.ma>
ssb   ed25519/F0D7A2F02CAE648A 2021-11-14 [S] [expires: 2026-11-13] # <- [S] 鍵の ed25519/{ここ}
#...

$ git config --global user.signingkey F0D7A2F02CAE648A
```

または `~/.gitconfig` を直接編集することもできます。

```~/.gitconfig
[user]
    #...
    signingkey = F0D7A2F02CAE648A
    #...
[commit]
    gpgsign = true
    #...
```

この状態で `git commit` とすると、勝手に署名処理が走ります。便利。

以上で Git コミットに関する手順は終わりです。

### 認証鍵を SSH に使う
この設定をすると、 GPG の認証用副鍵を使って SSH の公開鍵認証を行うことができます。

やることとしては簡単で、大きく４ステップです。

#### 1. GPG Agent 側から SSH の使用を有効化する
GPG Agent は SSH Agent のような存在で、 GPG 鍵のパスフレーズ入力を省略するなど、いろいろな管理機能を持っています。この GPG Agent が SSH を扱えるように設定します。

```~/.gnupg/gpg-agent.conf
enable-ssh-support
```

#### 2. 認証鍵の Keygrip を調べる
Keygrip は GPG Agent 側が鍵を識別するための文字列です。

```sh
$ gpg --list-key --with-keygrip
/Users/kino-ma/.gnupg/pubring.kbx
---------------------------------
#...
sub   ed25519 2021-11-14 [A] [expires: 2026-11-13]
      Keygrip = ABCDEFG1234567...  # <- 認証鍵鍵 ([A]) の keygrip をコピーする
```

#### 3. GPG Agent が SSH 用に認証鍵を読み込むようにする
GPG Agent は、 `~/gnupg/sshcontrol` に書かれたキーグリップを持つ鍵で SSH 認証を行います。

```~/gnupg/sshcontrol
#...
# 使い方とか書いてある
#...
{keygrip} # ABCDEFG1234567...
```

#### 4. SSH Agent の環境変数を export する
```sh
# .zshrc 等
export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
```

SSH Agent はこの `$SSH_AUTH_SOCK` を見て通信するらしいです。

#### (optional) Pinentry の設定をする
本題からは外れますが、 SSH Agent と通信するときは GPG 鍵パスフレーズ等のプロンプトがうまくいかないことがあるので、 [Pinentry の設定](https://qiita.com/kino-ma/items/0f10112e0aaef3e39387) をするといいかもしれないです（手前味噌ですが）。

#### 試す
ここまできたら、 GPG の認証鍵を使って SSH できるはずです。 `gpg --export-ssh-key 名前` で公開鍵を出力して、リモートマシンに登録しましょう。

```sh
local:~$ gpgconf --kill gpg-agent  # GPG Agent再起動
local:~$ ssh remote
# パスフレーズを求められたりする
remote:~$ hostname
remote
```

一発でうまくいかない場合もあると思います。デバッグしたいときは `~/.ssh/id_rsa` を `~/.ssh/id_rsa_` に一時的にリネームするなどして自動で読み込まれないようにすると、やりやすいです。

SSH に関する手順は以上です。

## まとめ
本記事では、 GPG の概要や嬉しさ、筆者が GPG を使い始めた時の手順を紹介しました。

経験と理解が浅く、かつ突貫工事で記事を用意したため、読み苦しい点が多かったと思います。そんな中で最後まで目を通してくださりありがとうございました。

本記事を読んで GPG に入門する人が現れたら嬉しいです。また、訂正・コメント等あれば残していただけると幸いです。

以上です。