YubiKey + GPG + GitHub 署名: クイックスタート


**所要時間目安: 10〜15 分**

---

## 0. 事前準備

* **ハードウェア**: YubiKey 5 シリーズ（OpenPGP 3.x 対応）
* **ソフトウェア**

  * GnuPG 2.4 以降  （`gpg --version` で確認）
  * YubiKey Manager (`ykman`)
  * pinentry（macOS は `pinentry-mac`）
  * Git 2.35 以降

> macOS なら `brew install gnupg yubikey-manager pinentry-mac`

---

## 1. YubiKey OpenPGP アプレットの初期化 (初回のみ)

```bash
# **すべての OpenPGP データが消えます**
ykman openpgp reset --force

# 管理者 PIN / ユーザ PIN を変更
# 既定 admin PIN: 12345678, user PIN: 123456
ykman openpgp access change-pin --pin 123456 --new-pin <NEW_PIN>
ykman openpgp access change-admin-pin --admin-pin 12345678 --new-admin-pin <NEW_ADMIN_PIN>

# カード情報を設定（任意）
gpg --card-edit
> admin
> name
姓 名
> lang
ja
> quit
```

---

## 2. マスター鍵とサブ鍵を作成

> **推奨**: オフライン PC でマスター鍵を作成し、サブ鍵のみ YubiKey に移動する。

```bash
gpg --full-generate-key
# 1) (9) ECC and ECC
# 2) (1) Curve25519 (ed25519)
# 3) 用途: SC (認証は後で追加)
# 4) 有効期限: 3y など
# 5) 名前とメール (GitHub 用メール)
```

### KEYID の取得方法

生成した鍵の KEYID は以下のコマンドで確認できます（**LONG 形式がおすすめ**）：

```bash
# 公開鍵一覧を長い形式で表示
gpg --list-keys --keyid-format LONG

# 秘密鍵（サブ鍵含む）の一覧
gpg --list-secret-keys --keyid-format LONG
```

出力例:

```
sec   ed25519/ABCD1234EFGH5678 2025-05-12 [SC]
      0123 4567 89AB CDEF 0123  4567 89AB CDEF 0123 4567
```

`ed25519/` の後ろに続く **16 桁が LONG KEYID**（8 桁は SHORT）。この値を `<KEYID>` の箇所に置き換えてください。

---

### サブ鍵を追加（署名・暗号・認証）

```bash
# 署名サブ鍵
gpg --edit-key <KEYID>
gpg> addkey
# → (10) ECC (sign only) を選択
#   Curve25519 を選び、期限を設定
#   ==> 署名 (S) 用サブ鍵完成
gpg> save

> **keytocard を実行すると?**  
> 選択したサブ鍵の **秘密鍵本体はローカルから完全に削除** され、カード側にのみ保存されます。ローカルには **スタブ (鍵参照) だけ** が残り、`gpg --list-secret-keys` で `ssb>` と `card-no:` が付いた行として表示されます。したがって YubiKey を紛失すると復元できないため、必ず Step 4 の手順で秘密鍵バックアップを作成してから削除・移行を行ってください。

# 暗号サブ鍵
gpg --edit-key <KEYID>
gpg> addkey
# → (12) ECC (encrypt only) を選択
#   Curve25519 を選び、期限を設定
#   ==> 暗号 (E) 用サブ鍵完成
gpg> save

# 認証サブ鍵
gpg --edit-key <KEYID>
gpg> addkey
# → (10) ECC (sign only) を選択
#   Curve25519 を選ぶ → 途中で Capabilities 画面が出たら `S` を無効化し `A` を有効化
#   （expert モードの場合は `toggle` で切替可能）
#   ==> 認証 (A) 用サブ鍵完成
gpg> save
```

> **Point**: GnuPG 2.4 系では `addkey` のメニュー番号が `(10) ECC (sign only)` / `(12) ECC (encrypt only)` などに変わっています。上記番号を参考に、署名用・暗号用・認証用の 3 つのサブ鍵を個別に作成してください。

---

## 3. サブ鍵を YubiKey に移動

> **キー番号とカードスロットの対応**
>
> * 署名サブ鍵 → **Signature slot** (= `keytocard (1)`)
> * 暗号サブ鍵 → **Encryption slot** (= `keytocard (2)`)
> * 認証サブ鍵 → **Authentication slot** (= `keytocard (3)`)
>   カード側に既に鍵が入っているスロットは選択肢に出てきません。表示される番号は空いているスロットのみです。

```bash
gpg --edit-key <KEYID>
# 秘密鍵を表示
gpg> toggle           

# 1. 署名サブ鍵を選択しカードへ移動
gpg> key 1            # "ssb  ... usage: S" を選択
gpg> keytocard        # プロンプト: (1) Signature key  を選択

# 2. 暗号サブ鍵を選択しカードへ移動
gpg> key 2            # "ssb  ... usage: E"
gpg> keytocard        # プロンプト: (2) Encryption key が表示されていれば選択

# 3. 認証サブ鍵を選択しカードへ移動
gpg> key 3            # "ssb  ... usage: A"
gpg> keytocard        # プロンプト: (3) Authentication key

gpg> save
```

**例**: 署名サブ鍵を選択 (`key 2`) したところ `keytocard` で

```
Please select where to store the key:
   (1) Signature key
   (3) Authentication key
Your selection?
```

と出た場合、**署名用なら 1**、認証用として使いたいなら 3 を選びます。

---

### 移行後の検証

```bash
# カードに格納された鍵情報を確認
gpg --card-status
```

* **Signature key / Encryption key / Authentication key** の 3 行すべてに鍵指紋が表示され、`created:` が表示されていればカードへの移行完了です。
* `Authentication key: [none]` と出る場合は認証サブ鍵がまだカードに移動していません。再度 `key 3` → `keytocard` → `(3) Authentication key` を実行してください。
* `ssb>` 行に `card-no: xxxx` が付いているサブ鍵は **カードに格納済み** を示します。

```bash
# サブ鍵が "ssb>" (先頭 >) と表示され card-no が付いているか確認
gpg --list-secret-keys --keyid-format LONG
```

動作テスト:

```bash
# 署名テスト
echo "hello" | gpg --clearsign | gpg --verify -

# 暗号化テスト (自己宛)
 echo "secret" | gpg -e -r <KEYID> | gpg -d
```

> **SSH 認証を使うなら**: 認証サブ鍵を移動したあと、`echo "enable-ssh-support" >> ~/.gnupg/gpg-agent.conf && killall gpg-agent` で gpg-agent を SSH エージェントとして有効化し、`ssh-add -L` で鍵が表示されるか確認します。

---

## 4. マスター鍵を安全にバックアップ & 削除

```bash
# 公開鍵
gpg --armor --export <KEYID> > pubkey.asc
# 秘密鍵バックアップ（オフライン保管推奨）
gpg --export-secret-keys <KEYID> > masterkey-backup.gpg
gpg --export-ownertrust > ownertrust.txt
# PC から秘密鍵を削除
gpg --delete-secret-keys <KEYID>
```

---

## 5. GitHub に公開鍵を登録

```bash
gpg --armor --export <KEYID> | pbcopy   # Linux は xclip
```

1. GitHub → **Settings → SSH and GPG keys → New GPG key**
2. コピーした公開鍵を貼り付け **Add GPG key**

---

## 6. Git を設定

```bash
git config --global user.signingkey <KEYID>
git config --global commit.gpgsign true
git config --global gpg.program gpg

# macOS: pinentry を固定したい場合
echo "pinentry-program /opt/homebrew/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
killall gpg-agent
```

シェルに以下を追加（bash/zsh 共通）:

```bash
echo 'export GPG_TTY=$(tty)' >> ~/.zshrc
eval "$(grep GPG_TTY ~/.zshrc)"
```

---

## 7. コミットを署名

```bash
git commit -S -m "Add README"
# 初回はカード PIN を要求される
```

push 後、GitHub 上で **Verified** バッジが表示されることを確認。

---

## 8. トラブルシューティング

| 症状                                               | 解決策                                                                                                                                                                                                                |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `signing failed: Inappropriate ioctl for device` | `export GPG_TTY=$(tty)` をシェル設定                                                                                                                                                                                     |
| `gpg: signing failed: card error`                | YubiKey を挿し直す / PIN ロックを確認                                                                                                                                                                                         |
| GitHub で「Unverified」                             | 公開鍵メールとコミットメールが一致しているか確認                                                                                                                                                                                           |
| `gpg: KEYTOCARD failed: Unusable secret key`     | 選択したサブ鍵はすでにカードに移動済み (`ssb>` + `card-no:`) か、ローカルに秘密鍵が残っていない。`gpg --list-secret-keys --keyid-format LONG` で `ssb>` 状態を確認し、まだ `card-no:` が無い認証サブ鍵 (`usage: A`) を選んで `keytocard (3)` を実行するか、バックアップから秘密鍵を再インポートしてから再試行 |

---

## 9. 2 本目の YubiKey でバックアップ運用

鍵ペアは **一度カードに移すと取り出せない** ため、バックアップ用 YubiKey を作る場合は次の手順で “鍵バックアップ → 2 本目へ再書き込み” を行います。

> 前提: Step 4 で `masterkey-backup.gpg` と `ownertrust.txt` を安全な場所に保管済み。

### 手順（*まだカードに移していない鍵がローカルに残っている前提*）

> **重要**: バックアップ用鍵を作る場合、**最初の YubiKey に `keytocard` する前に** このセクションの作業を実施してください。`keytocard` した後では秘密鍵本体が失われるため、バックアップとして 2 本目にコピーできません（後述の回避策を参照）。

1. **2 本目 YubiKey を初期化**

```bash
ykman openpgp reset --force
ykman openpgp access change-pin --pin 123456 --new-pin <NEW_PIN_2>
ykman openpgp access change-admin-pin --admin-pin 12345678 --new-admin-pin <NEW_ADMIN_PIN_2>
```

2. **オフライン PC でバックアップ鍵を復元**
   *ここで秘密鍵とサブ鍵がローカルに“再び”存在する状態になるため、続く ****`keytocard`**** で 2 本目に書き込めます。*

```bash
gpg --import masterkey-backup.gpg
gpg --import-ownertrust ownertrust.txt
```

3. **鍵を 2 本目 YubiKey へ移動** — Step 3 と同じく `keytocard` で 3 スロットに書き込み。

```bash
gpg --edit-key <KEYID>
gpg> toggle
gpg> key 1  # 署名
gpg> keytocard (1)
# ...（暗号・認証サブ鍵も繰り返し）
gpg> save
```

4. **ローカル秘密鍵を再削除**（忘れずに！）

```bash
gpg --delete-secret-keys <KEYID>
```

5. **動作確認**

```bash
gpg --card-status  # 2 本目でも指紋が 3 行そろうか確認
gpg --sign -u <KEYID> -o /tmp/test.sig /etc/hosts && gpg --verify /tmp/test.sig
```

> **Tips**
>
> * 2 本の YubiKey は PIN を変えておくと管理しやすいです。
> * 普段はバックアップキーをオフサイト（金庫等）に保管し、緊急時のみ使用する運用が安全です。

---

## 10. 参考リンク

* [Yubico Docs – OpenPGP Quickstart](https://support.yubico.com/)
* [GitHub Docs – Signing commits](https://docs.github.com/)

---

Happy hacking! \:key: