@@ -13,6 +13,9 @@ Git のコミット署名鍵を macOS の Secure Enclave で管理する。1Pass
1313- ` ssh-keygen ` に ` SSH_SK_PROVIDER ` で dylib を教えるだけなので、** 常駐エージェントは不要**
1414 (Secretive のような GUI アプリも login item も要らない)。
1515- ` ~/.ssh/id_git_sign ` は鍵ハンドルであって秘密鍵ではない。秘密鍵は Secure Enclave から出せない。
16+ ただし現在の ` -t none ` は使用時認証を行わないため、同じユーザー権限で動くプロセスは
17+ ユーザー操作なしで署名を要求できる。Secure Enclave が防ぐのは秘密鍵の持ち出しであり、
18+ ローカルプロセスによる鍵の使用ではない。
1619
1720### 前提
1821
@@ -26,52 +29,58 @@ Git のコミット署名鍵を macOS の Secure Enclave で管理する。1Pass
2629
2730| 項目 | 値 |
2831| ---| ---|
29- | ホスト | M4Pro のみ (M4Air は 1Password のまま) |
32+ | M4Pro の signer | Secure Enclave (` hosts/M4Pro/default.nix ` で共有設定を上書き) |
33+ | M4Air の signer | 1Password (` home/git.nix ` の共有既定値) |
3034| identity label | ` git-sign ` |
31- | public key hash (SHA1) | ` D93C59C037113F3418EAAB2E4EA5277EB34DCE1B ` |
32- | SSH fingerprint | ` SHA256:AsbsNi30tI7r4kUqNSHew9DI86raUYeHeYP3MLCjpck ` |
35+ | private key protection | ` none ` (署名時の Touch ID なし) |
36+ | public key hash (SHA1) | ` D93C59C037113F3418EAAB2E4EA5277EB34DCE1B ` (現在の M4Pro の値) |
37+ | SSH fingerprint | ` SHA256:AsbsNi30tI7r4kUqNSHew9DI86raUYeHeYP3MLCjpck ` (現在の M4Pro の値) |
3338| 証明書有効期限 | ** 2036/08/07** (作成時は 2027/08/10 = 1年。下記手順で10年に更新済み) |
34- | GitHub signing key | id=1103015 ` Secure Enclave (M4Pro, signing) ` |
39+ | GitHub signing key | M4Pro: id=1103015 ` Secure Enclave (M4Pro, signing) ` / M4Air: id=805051 ` 1Password (signing) ` |
40+ | allowed signers | ` home/git.nix ` の ` allowedSignerKeys ` から生成 |
3541
36- ### 移行の進捗
42+ ` ~/.ssh/allowed_signers ` は Home Manager が Nix store へのシンボリックリンクとして管理する。
43+ 鍵を追加・変更するときはこのファイルを直接編集せず、` home/git.nix ` を更新して rebuild する。
3744
38- | ステップ | 状態 |
39- | ---| ---|
40- | Secure Enclave に鍵作成 | 完了 |
41- | ` ~/.ssh/id_git_sign{,.pub} ` 配置 | 完了 |
42- | GitHub に Signing Key 登録・` verified=true ` 確認 | 完了 |
43- | 証明書を10年に更新 (2036/08/07) | 完了 |
44- | ` home/git.nix ` の切り替え | ** 未実施** (まだ ` op-ssh-sign ` を使っている) |
45-
46- ### git 側の設定 (切り替え時)
45+ ### Git 側の設定
4746
48- ` ssh-keygen ` に ` SSH_SK_PROVIDER ` を渡すラッパーが要る。git は ` gpg.ssh.program ` に
49- 環境変数を渡せないため。
50-
51- ``` sh
52- #! /bin/sh
53- export SSH_SK_PROVIDER=/usr/lib/ssh-keychain.dylib
54- exec /usr/bin/ssh-keygen " $@ "
55- ```
47+ ` ssh-keygen ` に ` SSH_SK_PROVIDER ` を渡すラッパーが要る。Git は ` gpg.ssh.program ` に
48+ 環境変数を渡せないため、` hosts/M4Pro/default.nix ` の ` sshSign ` でラッパーを生成している。
5649
5750` user.signingkey ` は 1Password 時代のリテラル公開鍵ではなく、** ハンドルファイルのパス**
5851(` ~/.ssh/id_git_sign ` ) を指定する。sk 鍵はハンドルが無いと署名できない。
5952
6053この鍵は M4Pro にしか存在しないので、` home/git.nix ` (全ホスト共有) に直接書くと
61- ** M4Air が次の rebuild でコミット不能になる** 。ホスト別に分けること。
54+ ** M4Air が次の rebuild でコミット不能になる** 。そのため共有設定は 1Password を
55+ ` lib.mkDefault ` のまま維持し、M4Pro のホスト設定だけで鍵と signer を上書きしている。
56+
57+ ### Signing key のローテーション
58+
59+ GitHub の ` id=805051 ` ` 1Password (signing) ` は M4Air が現在も使う** 現役鍵** なので、
60+ M4Air を移行するまでは削除しない。GitHub は一度 Verified にしたコミットの検証記録を
61+ repository network 内で永続化するため、登録鍵を後から削除しても、その記録がある過去コミットは
62+ Verified のままになる。ただし削除後に初めて push するコミットや、別の repository network で
63+ 初めて検証するコミットには登録鍵が必要になる。
64+
65+ ローカル検証は GitHub と異なり、毎回 ` ~/.ssh/allowed_signers ` を参照する。鍵を作り直したら
66+ ` home/git.nix ` の ` allowedSignerKeys ` に新しい公開鍵を** 追記** し、過去の署名を検証するために
67+ 古い鍵も残す。鍵が全ホストで退役した後は、古い鍵の先頭に
68+ ` valid-before="YYYYMMDDHHMMSSZ" ` を付けると、切り替え前の署名だけを有効にできる。
69+ ` allowedSignerKeys ` では
70+ ` "valid-before=\"YYYYMMDDHHMMSSZ\" ssh-ed25519 AAAA..." ` のように引用符をエスケープする。
6271
63- ### GitHub 側の不変条件
72+ 参考:
6473
65- ** 旧 signing key ` id=805051 ` "1Password (signing)" (ssh-ed25519) は削除しないこと。**
66- 削除すると、その鍵で署名した過去のコミットがすべて Unverified に落ちる。
67- 鍵をローテーションした場合も同様に、古い登録は残したまま新しい鍵を追加する。
74+ - [ GitHub: Persistent commit signature verification] ( https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification#persistent-commit-signature-verification )
75+ - [ Git: ` gpg.ssh.allowedSignersFile ` ] ( https://git-scm.com/docs/git-config#Documentation/git-config.txt-gpgsshallowedSignersFile )
6876
6977## 新規セットアップ
7078
7179``` bash
7280# 1. Secure Enclave に鍵を作る
7381# -k p-256-ne : ECDSA P-256 / non-exportable (Secure Enclave は P-256 のみ対応)
74- # -t none : Touch ID を要求しない (-t bio にするとコミットのたびに Touch ID)
82+ # -t none : 使用時認証なし。同じユーザー権限のプロセスは操作なしで署名できる
83+ # 使用時認証が必要なら -t bio にして署名ごとに Touch ID を要求する
7584sc_auth create-ctk-identity -l git-sign -k p-256-ne -t none -N git-sign
7685
7786# 2. SSH 鍵ハンドルを書き出す (カレントディレクトリに生成されるので注意)
@@ -85,10 +94,25 @@ mv id_ecdsa_sk_rk.pub ~/.ssh/id_git_sign.pub
8594gh api -X POST /user/ssh_signing_keys \
8695 -f title=" Secure Enclave ($( scutil --get LocalHostName) , signing)" \
8796 -f key=" $( cat ~ /.ssh/id_git_sign.pub) "
97+
98+ # 4. dotfiles に記録する値を表示する
99+ cat ~ /.ssh/id_git_sign.pub
100+ /usr/bin/ssh-keygen -lf ~ /.ssh/id_git_sign.pub
101+ sc_auth list-ctk-identities -t sha1 -e hex
102+ ```
103+
104+ 公開鍵の出力を ` home/git.nix ` の ` allowedSignerKeys ` に追記する。過去コミットの検証に必要なので、
105+ 既存の鍵は削除しない。併せて fingerprint、public key hash、GitHub key ID を上の「現在の構成」に
106+ 反映する。` home/git.nix ` とこの文書を保存した後、Home Manager を適用する。
107+
108+ ``` bash
109+ # 5. dotfiles を反映する
110+ nix run " $HOME /.dotfiles#switch"
88111```
89112
90- ` -t none ` でも署名には FIDO の user-presence フラグが立つため、` allowed_signers ` に
91- ` no-touch-required ` を付ける必要はない (付けると OpenSSH が ` unknown key option ` で落ちる)。
113+ ` -t none ` でも署名には FIDO の user-presence フラグが立つが、provider が操作なしで付けるため、
114+ 対話的な user presence の証明にはならない。` allowed_signers ` に ` no-touch-required ` を付ける必要はなく、
115+ 付けると OpenSSH が ` unknown key option ` で落ちる。
92116
93117作成直後の証明書は** 有効期限が1年** しかないので、続けて「証明書の更新」を実行して
9411810年に伸ばしておくこと。
@@ -119,13 +143,18 @@ SSH はこの証明書を使わない (生の公開鍵しか見ない) ので、
119143
120144``` bash
121145# 1. 更新前の状態を控える (あとで fingerprint 不変を確認するため)
122- sc_auth list-ctk-identities
146+ sc_auth list-ctk-identities -t sha1 -e hex
123147sc_auth list-ctk-identities -t ssh
124148
125149# 2. CSR を作る
126150# -h に渡すのは list-ctk-identities のデフォルト表示 = SHA1 (40桁)。
127151# -t sha256 -e hex の値を渡すと "No identity found for hash" で落ちる。
128- HASH=D93C59C037113F3418EAAB2E4EA5277EB34DCE1B
152+ # 直前の出力から CN=git-sign の identity に対応する値をコピーする。
153+ HASH=' <git-sign identity の SHA1 40桁>'
154+ if [[ ! $HASH =~ ^[0-9A-Fa-f]{40}$ ]]; then
155+ echo " HASH は40桁のSHA1を指定すること" >&2
156+ exit 1
157+ fi
129158cd " $( mktemp -d) "
130159sc_auth create-ctk-csr -h " $HASH " -f renew.csr -N git-sign
131160
@@ -172,11 +201,21 @@ GitHub への再登録が必要になる。
172201# identity が 2 本あると ssh-keygen -K が両方を同じ id_ecdsa_sk_rk に書こうとして
173202# 上書きプロンプトで詰まる。KEYCHAIN_CERTIFICATES で絞る方法は -w の Secure Key 経路では
174203# 効かなかった。
175- sc_auth delete-ctk-identity -h D93C59C037113F3418EAAB2E4EA5277EB34DCE1B
176-
177- # 2. 「新規セットアップ」を最初からやり直す
178-
179- # 3. GitHub の古い signing key は削除しないこと (過去コミットが Unverified になる)
204+ cat ~ /.ssh/id_git_sign.pub
205+ sc_auth list-ctk-identities -t sha1 -e hex
206+ OLD_HASH=' <削除する git-sign identity の SHA1 40桁>'
207+ if [[ ! $OLD_HASH =~ ^[0-9A-Fa-f]{40}$ ]]; then
208+ echo " OLD_HASH は40桁のSHA1を指定すること" >&2
209+ exit 1
210+ fi
211+ printf ' 削除対象: %s\n' " $OLD_HASH "
212+ sc_auth delete-ctk-identity -h " $OLD_HASH "
213+
214+ # 2. 「新規セットアップ」を最初からやり直す。GitHub への新鍵登録と
215+ # allowedSignerKeys への追記、Home Manager の反映まで実施する
216+
217+ # 3. 新旧の GitHub signing key を確認する
218+ # id=805051 は M4Air が現役利用している間は削除しない
180219gh api /user/ssh_signing_keys --jq ' .[] | "\(.id)\t\(.title)"'
181220```
182221
@@ -201,17 +240,18 @@ gh auth refresh -h github.com -s admin:ssh_signing_key
201240
202241| 症状 | 原因 |
203242| ---| ---|
204- | ` No identity found for hash ` | ` -h ` に SHA256 を渡している。 SHA1 (40桁) を使う |
243+ | ` No identity found for hash ` | ` -h ` に SHA256 や別端末の値を渡している。対象 identity の SHA1 (40桁) を使う |
205244| ` ssh-keygen -K ` が上書きプロンプトで止まる | CTK identity が複数ある。不要なものを削除する |
206245| ` allowed_signers:1: bad options: unknown key option ` | ` no-touch-required ` を書いている。不要 |
246+ | ` No principal matched ` | 新しい公開鍵が ` home/git.nix ` の ` allowedSignerKeys ` に無いか、rebuild が未実施 |
207247| コミットが署名エラーで落ちる | ` ~/.ssh/id_git_sign ` の消失、` ssh-keychain.dylib ` のパス変更、identity の消失を先に疑う。` sc_auth list-ctk-identities ` で存在と ` Valid ` を確認 |
208248| nix で openssh を入れた直後に壊れた | ` ssh-keygen ` が nix 側に解決されている。` /usr/bin/ssh-keygen ` を絶対パスで呼ぶ |
209249
210250## 検証済み/未検証の区別
211251
212252このドキュメントのうち実測で確認したもの:
213253
214- - 新規セットアップ一式 (鍵作成 → 書き出し → 署名 → 検証 → GitHub で ` verified=true ` )
254+ - 新規セットアップ一式 (鍵作成 → 書き出し → GitHub/ ` allowed_signers ` 登録 → 署名・検証 )
215255- 証明書更新の全工程を本番 identity ` git-sign ` で実行し、以下を確認 (2026-08-10):
216256 - 有効期限が 2027/08/10 → 2036/08/07 に伸びた
217257 - public key hash / SSH fingerprint / CN が** すべて不変**
0 commit comments