Featured image of post ぼくの考えた最強の
GPG Key 環境 for Windows 11

ぼくの考えた最強の GPG Key 環境 for Windows 11

Summary

上記で、 Windows 11 環境にひと通り環境構築したわけですが ssh-agent を利用して開発環境用に Remote のサーバーに ForwardAgent で転送して使うと ssh-add -l など挙動をした時にハングアップしてしまい対策に困っていた。まだ完全とはいかないが少なくても以前よりは安定した状態にできたのでこちらにまとめる。

OpenSSH のバージョンアップ

Windows 10 から OpenSSH が付属している。 がこれは、皆さんがよく使っている Linux に付属する OpenSSH と違い Microsoft が Windows 用に実装した OpenSSH である。これの利点は混沌としている、 Windows の SSH 環境で使えるように実装されており NamedPipe を利用して ssh-agent 機能が提供できるようになっている。

ここまではいいのだが、 Windows 11 付属の OpenSSH は version 8.6p1, LibreSSL 3.4.3 となっているため Ubuntu 22.04 などと合わせて使ったり WSL2 上に Ubuntu 22.04 を立てて使う場合最新版までの間に追加された拡張命令が来た瞬間に応答を停止するなど事象がある。

そのため、個人的にはアップデートをおすすめする。 また今回の version は共存できるが、 Shell の Path 順序での判定で気持ち悪いので標準付属の物はアンインストールしてしまうことにした。

OpenSSH 8.6p のアンインストール

1
2
> ssh -V
OpenSSH_for_Windows_8.6p1, LibreSSL 3.4.3
Note

PowerShell 7 だとうまくいかず、下記のエラーを吐きます

1
Get-WindowsCapability: クラスが登録されていません

そのため、明示的に powershell.exe を呼び出し実行とします

1
> powershell.exe
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#
# 管理者権限プロンプトを立ち上げて作業
#

> powershell.exe
> Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'

Name  : OpenSSH.Client~~~~0.0.1.0
State : Installed

Name  : OpenSSH.Server~~~~0.0.1.0
State : NotPresent

> Remove-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0

Path          :
Online        : True
RestartNeeded : True

> Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'

Name  : OpenSSH.Client~~~~0.0.1.0
State : UninstallPending

Name  : OpenSSH.Server~~~~0.0.1.0
State : NotPresent
Topic
ここで一度再起動が必要

OpenSSH 9.5p のインストール

winget でインストールをおすすめする。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
> winget search openssh
名前         ID                     バージョン 一致         ソース
------------------------------------------------------------------
SSHells      dzonder.sshells        0.1.4      Tag: openssh winget
OpenSSH Beta Microsoft.OpenSSH.Beta 9.5.0.0                 winget

> winget install Microsoft.OpenSSH.Beta
見つかりました OpenSSH Beta [Microsoft.OpenSSH.Beta] バージョン 9.5.0.0
このアプリケーションは所有者からライセンス供与されます
Microsoft はサードパーティのパッケージに対して責任を負わずライセンスも付与しません
ダウンロード中 https://github.com/PowerShell/Win32-OpenSSH/releases/download/v9.5.0.0p1-Beta/OpenSSH-Win64-v9.5.0.0.msi
  ██████████████████████████████  5.47 MB / 5.47 MB
インストーラーハッシュが正常に検証されました
パッケージのインストールを開始しています...
インストールが完了しました

これで標準付属のやつはアンインストールされ 9.5p がインストールされた状態になるはずのためアンインストールが出来ているか確認する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#
# 管理者権限プロンプトを立ち上げて作業
#

> powershell.exe
> Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'

Name  : OpenSSH.Client~~~~0.0.1.0
State : NotPresent

Name  : OpenSSH.Server~~~~0.0.1.0
State : NotPresent
Note
再起動後、 新しくインストールした OpenSSH 9.5.0.0 の ssh-agent と sshd が起動しているので これの自動起動を停止、しサービスを停止する。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#
# 管理者権限プロンプトを立ち上げて作業
#

> Get-Service | Where-Object {$_.Name -match '(CertPropSvc|SCardSvr|ssh-agent|sshd)'} | Select Status, Name, StartType, DisplayName

 Status Name        StartType DisplayName
 ------ ----        --------- -----------
Stopped CertPropSvc Automatic Certificate Propagation
Running SCardSvr    Automatic Smart Card
Stopped ssh-agent   Automatic OpenSSH Authentication Agent
Stopped sshd        Automatic OpenSSH SSH Server


# 自動起動の無効化
> Set-Service -Name "CertPropSvc" -StartupType Disabled
> Stop-Service -Name "CertPropSvc"

> Set-Service -Name "ssh-agent" -StartupType Disabled
> Stop-Service -Name "ssh-agent"
> Set-Service -Name "sshd" -StartupType Disabled
> Stop-Service -Name "sshd"

SCardSvr のみ自動起動で、他は Disabled になっていれば問題ありません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#
# 管理者権限プロンプトを立ち上げて作業
#

# 起動設定確認
> Get-Service | Where-Object {$_.Name -match '(CertPropSvc|SCardSvr|ssh-agent|sshd)'} | Select Status, Name, StartType, DisplayName
 Status Name        StartType DisplayName
 ------ ----        --------- -----------
Stopped CertPropSvc  Disabled Certificate Propagation
Stopped SCardSvr    Automatic Smart Card
Stopped ssh-agent    Disabled OpenSSH Authentication Agent
Stopped sshd         Disabled OpenSSH SSH Server

gpg-agent 設定

$env:APPDATA\gnupg\gpg-agent.conf に gpg-agent.conf を作成します。 この時 enable-win32-openssh-support を追加することで Windows 搭載の OpenSSH からも利用することができるようになります。 enable-ssh-support は Windows では機能しないため、記載する必要はありません

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$confContent = @"
# Ref: https://www.gnupg.org/documentation/manuals/gnupg/Agent-Options.html

###+++--- GPGConf ---+++###
enable-putty-support
enable-win32-openssh-support

# TTL   3600 (1 hour),  7200 (2 hour), 10800 (3 hour), 14400 (4 hour)
#      18000 (5 hour), 21600 (6 hour), 25200 (7 hour), 28800 (8 hour)
# cache
default-cache-ttl 3600
max-cache-ttl 21600

# cache ssh
default-cache-ttl-ssh 3600
max-cache-ttl-ssh 21600

# Disable Pinentry external cache
no-allow-external-cache
###+++--- GPGConf ---+++###
# GPGConf edited this configuration file.
# It will disable options before this marked block, but it will
# never change anything below these lines.

"@

New-Item "$env:APPDATA\gnupg" -ItemType Directory
Set-Content -Path $env:APPDATA\gnupg\gpg-agent.conf -Value $confContent

Scdaemon 設定

PC/SC(Personal Computer/Smart Card) を gpg-agent で使う場合 PC/SC を専有する設定になっているようなので、共有するように変更し gpg-agent に内蔵されている CCID ドライバーを無効化し Windows 標準の Smart Card サービスを使うように設定します。

YubiKey のデバイス名

YubiKey の Device Name が Series 4, 5 で名前が違うらしいため、自身の利用する YubiKey を刺して確認する 私の、 YubiKey 5 NFC は Yubico YubiKey でしたのでこちらを後で使います。

1
2
3
> Get-PnpDevice -Class SoftwareDevice | Where-Object {$_.FriendlyName -like "*YubiKey*"} | `
  Select-Object -ExpandProperty FriendlyName
Yubico YubiKey OTP+FIDO+CCID 0

設定ファイルの書き出し

Yubico から設定記事が出ているので、これを参考にする

  • pcsc-shared を設定すると gpg-agent による PIN Cache がされず、毎回入力しなければならないので設定しない。
  • card-timeout は version 2.x で非推奨項目ですでに機能していないため不要
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$confContent = @"
# Ref: https://www.gnupg.org/documentation/manuals/gnupg/Scdaemon-Options.html

###+++--- ScdaemonConf ---+++###
disable-ccid
reader-port Yubico YubiKey

###+++--- ScdaemonConf ---+++###

"@

New-Item "$env:APPDATA\gnupg" -ItemType Directory
Set-Content -Path "$env:APPDATA\gnupg\scdaemon.conf" -Value $confContent

テスト

設定を変更したら下記コマンドで gpg-agent を再起動しましょう

1
2
3
4
5
6
7
8
# 設定の再読み込み
gpgconf --reload

# gpg-agent をすべて強制終了
gpg-connect-agent killagent /bye

# gpg-agent 1次起動 & debug
gpg-agent --daemon --verbose

YubiKey にアクセスできるか? を確認して、 YubiKey に設定してある公開鍵をダウンロードして配置します。 正しく動けば、公開鍵がインポートされます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
> gpg --card-edit
gpg: keybox'C:\\Users\\naa0yama\\AppData\\Roaming\\gnupg\\pubring.kbx'が作成されました

gpg/card> fetch
gpg: 鍵を'https://keybase.io/naa0yama/pgp_keys.asc'から要求
gpg: C:\\Users\\naa0yama\\AppData\\Roaming\\gnupg\\trustdb.gpg: 信用データベースができました
gpg: 鍵794676DEF45A4D7F: 公開鍵"Naoki Aoyama <9667078+naa0yama@users.noreply.github.com>"をインポートしました
gpg:           処理数の合計: 1
gpg:             インポート: 1

gpg/card> q

インポートされた鍵を確認してみます。 下記のように鍵が見えれば問題ありません、信頼しておきましょう

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
> gpg -k
C:\Users\naa0yama\AppData\Roaming\gnupg\pubring.kbx
---------------------------------------------------
pub   nistp521 2024-09-13 [C]
      65284A0A9205C52EBC16BBCF794676DEF45A4D7F
uid           [  不明  ] Naoki Aoyama <9667078+naa0yama@users.noreply.github.com>
sub   cv25519 2024-09-13 [E]
sub   ed25519 2024-09-13 [S]
sub   ed25519 2024-09-13 [A]

> gpg --key-edit <KEYID>
gpg> trust

  1 = 知らないまたは何とも言えない
  2 = 信用し ない
  3 = まぁまぁ信用する
  4 = 充分に信用する
  5 = 究極的に信用する
  m = メーンメニューに戻る

あなたの決定は? 5
本当にこの鍵を究極的に信用しますか? (y/N) y

[  不明  ] (1). Naoki Aoyama <9667078+naa0yama@users.noreply.github.com>
プログラムを再起動するまで表示された鍵の有効性は正しくないかもしれない
ということを念頭においてください

gpg> q

> gpg -k
gpg: 信用データベースの検査
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: 深さ: 0  有効性:   1  署名:   0  信用: 0-, 0q, 0n, 0m, 0f, 1u
C:\Users\naa0yama\AppData\Roaming\gnupg\pubring.kbx
---------------------------------------------------
pub   nistp521 2024-09-13 [C]
      65284A0A9205C52EBC16BBCF794676DEF45A4D7F
uid           [  究極  ] Naoki Aoyama <9667078+naa0yama@users.noreply.github.com>
sub   cv25519 2024-09-13 [E]
sub   ed25519 2024-09-13 [S]
sub   ed25519 2024-09-13 [A]
Topic
ここで再起動
1
2
3
4
5
# 起動して各 agent が started になっていることを確認する
> gpg-agent --daemon --verbose
gpg-agent[24196]: gpg-agent (GnuPG) 2.4.5 started
gpg-agent[24196]: putty message loop thread started
gpg-agent[24196]: Win32-OpenSSH thread started

上記を実施したあとに別タブで ssh-add -l を実行すると下記のよう表示される。 表示を確認でしたら一度 gpg-agent を終了する

1
2
3
# 下記のように cardno:XX_XXX_XXX の表記がある鍵が1つ見えるはず
> ssh-add -l
256 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX cardno:XX_XXX_XXX (ED25519)

スタートアップ登録

スタートアップに登録する gpg-connect-agent.ps1 という名前で PowerShell スクリプトを作成し、ショートカット経由で自動起動するようにしておく。クリックする事に一度全て終了させて起動するようにしているのがトラブル回避とこだわりポイント。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# スクリプト本体を作成
$scriptContent = @'
# gpg-connect-agent.ps1

# kill
gpgconf --kill gpg-agent
gpgconf --kill scdaemon
Start-Sleep -Seconds 2

# starting
Start-Process -WindowStyle Hidden -FilePath "gpg-connect-agent" -ArgumentList "/bye"
'@

$scriptPath = "$env:APPDATA\gnupg\gpg-connect-agent.ps1"
Set-Content -Path $scriptPath -Value $scriptContent

# スタートアップにショートカットを作成(ウィンドウ非表示で実行)
$WshShell = New-Object -ComObject WScript.Shell
$shortcut = $WshShell.CreateShortcut("$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\gpg-connect-agent.lnk")
$shortcut.TargetPath = "pwsh.exe"
$shortcut.Arguments = "-WindowStyle Hidden -ExecutionPolicy Bypass -File `"$scriptPath`""
$shortcut.WindowStyle = 7  # Minimized
$shortcut.Save()

登録したスタートアップを Win + R を実行し shell:startup にある作成した gpg-connect-agent.lnk を起動する。

gpg-agent が稼働していることを確認

1
2
3
4
5
> Get-Process -Name 'gpg-agent'

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     17     3.86      12.85       0.16    1668   1 gpg-agent

Windows Terminal に戻って ssh-add -l を実行すると先程実行した時と同様に鍵が1つ見える

1
2
> ssh-add -l
256 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX cardno:XX_XXX_XXX (ED25519)

利用方法

Git 設定(Windows)

SSH Signing keys を利用するように設定します。

本来であれば真の意味の gpg-agent を利用する OpenGPG コミット署名をするのが目的になる。が、 Windows 環境では gpg-agent が待ち受けているプロトコルが特殊になっており、 WSL2 上にブリッジすることも、 remote SSH 先に RemoteForward して使う事もできない。そのため、強引な手法として Git の version 2.34.0 (2021-11-15) から OpenSSH 8.1 以降( 8.7 以上が推奨) との組み合わせで ssh-key による署名が可能になった。

だが、通常設定の gpg-agentssh-add で表示されるのは Authenticate 鍵のみであり、 Sign 鍵は見えない。

1
2
> ssh-add -l
256 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX cardno:XX_XXX_XXX (ED25519)

これを sshcontrol という設定ファイルに記述することで鍵を認識させ表示させる。
まず、Sign鍵の Keygrip を確認する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
> gpg --with-keygrip --list-secret-keys
[keyboxd]
---------
sec#  nistp521 2024-09-13 [C]
      65284A0A9205C52EBC16BBCF794676DEF45A4D7F
      Keygrip = 9DXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
uid           [  究極  ] Naoki Aoyama <9667078+naa0yama@users.noreply.github.com>
ssb>  cv25519 2024-09-13 [E]
      Keygrip = 65XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
ssb>  ed25519 2024-09-13 [S]
      Keygrip = CBXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
ssb>  ed25519 2024-09-13 [A]
      Keygrip = F3XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

OpenGPG の署名下記を確認して sshcontrol に記載する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
$lines = gpg --with-colons --with-keygrip --list-secret-keys
$uid = $null
$subkey = $null
$keygrip = $null
$captureNext = $false

foreach ($line in $lines) {
    $fields = $line -split ':'
    
    if ($fields[0] -eq 'uid') {
        $uid = $fields[9]
    }

    if ($fields[0] -eq 'ssb' -and $fields[11] -match 's') {
        $captureNext = $true
        $subkey = $($fields[4])
        Write-Host "Found signing subkey: $($fields[4])" -ForegroundColor Cyan
    }
    elseif ($captureNext -and $fields[0] -eq 'grp') {
        $keygrip = $fields[9]
        Write-Host "Extracted keygrip: $keygrip" -ForegroundColor Cyan
        break
    }
}

if (-not $keygrip) {
    Write-Host "Error: Signing key keygrip not found" -ForegroundColor Red
    exit 1
}

Write-Host "Find keygrip=$keygrip" -ForegroundColor Green

if (-not (Select-String -Path "$env:APPDATA\gnupg\sshcontrol" -Pattern $keygrip -Quiet 2>$null)) {
    $sshcontrolContent = @"

# OpenPGP Signature key (sign), KeyID: $subkey
# $uid
$keygrip
"@
    Add-Content -Path "$env:APPDATA\gnupg\sshcontrol" -Value $sshcontrolContent -Encoding ascii
    Write-Host "Signature key added to sshcontrol" -ForegroundColor Green
} else {
    Write-Host "Signature key already exists in sshcontrol" -ForegroundColor Yellow
}
Topic

gpg-agent を再起動する

1
Invoke-Item "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\gpg-connect-agent.lnk"

完了すると、鍵が2つ見えるようになるはずである。
(none) (ED25519) の鍵が先程 sshcontrol に追加した鍵

1
2
3
PS C:\Users\naa0yama> ssh-add -l
256 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX cardno:12_013_632 (ED25519)
256 SHA256:YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY (none) (ED25519)

その後 .gitconfig に必要設定を追加

1
2
# ファイルの事前作成
New-Item -ItemType File -Path ~/.gitignore_global -Force
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$keysing = (ssh-add -L | Select-String '\(none\)').Line -replace '\s+\(none\)$', ''
if ([string]::IsNullOrWhiteSpace($keysing)) {
    Write-Host "キーが見つかりませんでした"  -ForegroundColor Red
}
Write-Host "Find Signature key $keysing" -ForegroundColor Green

# ユーザー情報
git config --global user.email "9667078+naa0yama@users.noreply.github.com"
git config --global user.name "Naoki Aoyama"
git config --global user.signingkey "$keysing"
git config --global user.useConfigOnly true

# core
git config --global core.excludesfile ~/.gitignore_global

# SSH署名設定
git config --global gpg.format ssh
git config --global commit.gpgsign true
git config --global tag.gpgSign true
git config --global gpg.ssh.allowedSignersFile "~/.ssh/allowed_signers"

# pull & push
git config --global fetch.prune true
git config --global pull.rebase true
git config --global merge.ff false
git config --global rerere.enabled true
git config --global push.autoSetupRemote true
git config --global push.default current

# 新機能・最適化
git config --global branch.sort -committerdate
git config --global diff.algorithm histogram
git config --global init.defaultBranch main
git config --global maintenance.auto true
git config --global maintenance.strategy incremental
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$keysing = (ssh-add -L | Select-String '\(none\)').Line -replace '\s+\(none\)$', ''
$user_email = (git config --global --get user.email)
if ([string]::IsNullOrWhiteSpace($user_email)) {
    Write-Host "not set ~/.gitconfig user.email"  -ForegroundColor Red
}
Write-Host "Git user.email $user_email" -ForegroundColor Green

$gitconfigContent = @"
$user_email $keysing
"@

Set-Content -Path "$env:USERPROFILE\.ssh\allowed_signers" -Value $gitconfigContent
Windows Only
1
2
3
4
5
6
# Windows only
git config --global core.sshCommand "C:/Program Files/OpenSSH/ssh.exe"
git config --global gpg.ssh.program "C:/Program Files/OpenSSH/ssh-keygen.exe"

git config --global core.autocrlf true
git config --global core.safecrlf true

Git for Windows を入れてない場合は下記のように .gitconfig を作成することで代用します

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
$keysing = (ssh-add -L | Select-String '\(none\)').Line -replace '\s+\(none\)$', ''
if ([string]::IsNullOrWhiteSpace($keysing)) {
    Write-Host "キーが見つかりませんでした"  -ForegroundColor Red
}
Write-Host "Find Signature key $keysing" -ForegroundColor Green

$gitconfigContent = @"
[user]
    email = 9667078+naa0yama@users.noreply.github.com
    name = Naoki Aoyama
    signingkey = $keygrip
    useConfigOnly = true
[core]
    excludesfile = ~/.gitignore_global
[gpg]
    format = ssh
[commit]
    gpgsign = true
[tag]
    gpgSign = true
[gpg "ssh"]
    allowedSignersFile = ~/.ssh/allowed_signers
[fetch]
    prune = true
[pull]
    rebase = true
[merge]
    ff = false
[rerere]
    enabled = true
[push]
    autoSetupRemote = true
    default = current
[branch]
    sort = -committerdate
[diff]
    algorithm = histogram
[init]
    defaultBranch = main
[maintenance]
    auto = true
    strategy = incremental

"@
Set-Content -Path "$env:USERPROFILE\.gitconfig" -Value $gitconfigContent
Windows Only
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
New-Item -ItemType Directory -Path "$env:USERPROFILE\.gitconfig.d" -Force

$gitconfigContent = @"
[core]
    autocrlf = true
    safecrlf = true
    sshCommand = C:/Program Files/OpenSSH/ssh.exe
[gpg "ssh"]
    program = C:/Program Files/OpenSSH/ssh-keygen.exe

"@
Set-Content -Path "$env:USERPROFILE\.gitconfig.d/windows.ini" -Value $gitconfigContent

Commit テスト

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$testDir = Join-Path $env:TEMP "git-sign-test-$(Get-Date -Format 'yyyyMMddHHmmss')"
New-Item -ItemType Directory -Path $testDir
Set-Location $testDir

# 1. Gitリポジトリの初期化
git init

$env:GIT_TRACE = 1
Get-Date -Format "yyyyMMddHHmmss" | Out-File -FilePath README.md -Encoding utf8
git add README.md
git commit -m "test sign commit $(Get-Date -Format 'yyyyMMddHHmmss')"
$env:GIT_TRACE = $null

# 3. 署名の検証
Write-Host "`n=== コミットログ ===" -ForegroundColor Green
git log --show-signature -1

Write-Host "`n=== 詳細な署名検証 ===" -ForegroundColor Green
git verify-commit HEAD

Write-Host "`n=== コミット情報 ===" -ForegroundColor Green
git log --pretty=fuller -1

# 4. 後片付け
Write-Host "`n=== クリーンアップ ===" -ForegroundColor Yellow
Set-Location $env:TEMP
Remove-Item -Recurse -Force $testDir
Write-Host "テストディレクトリを削除しました: $testDir" -ForegroundColor Green

OpenSSH

OpenSSH Client

ssh-add -l を実行して鍵が見える場合はなにもしなくても利用できるはずです。 もうファイルで秘密鍵を置く必要はありません。

Server 側

踏み台サーバー上でも ssh-agent を使いたい場合は sshd に設定の追加が必要です

/etc/ssh/sshd_config
1
AllowAgentForwarding yes

追加後 sshd を再起動して設定の反映を確認します

1
2
3
4
5
6
> systemctl restart sshd

> sshd -T | grep allow
allowtcpforwarding yes
allowagentforwarding yes
allowstreamlocalforwarding yes

Tera Term(Pageant)

通常の方法でログイン方式の選択に「Pageantを使う」を選択すればよい

Tera Term の SSH認証

WSL2(OpenSSH -> wsl2-ssh-agent)

事前に wsl2-ssh-agent を導入すれば何もしなくても WSL2 内から鍵を利用できるはずです。 私は WSL2 をヘビーに使うので下記のレポジトリで管理しています。

1
2
3
4
5
user@hostname:~$ env | grep SSH_
SSH_AUTH_SOCK=/home/user/.ssh/wsl2-ssh-agent.sock

user@hostname:~$ ssh-add -l
256 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX cardno:XX_XXX_XXX (ED25519)

VSCode

VSCode で下記の2つを設定しておくと Commit 時に署名を強制できる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "git.enableCommitSigning": true, //                                Enables commit signing with GPG, X.509, or SSH
  "git.alwaysSignOff": true,       //                                Controls the signoff flag for all commits.

  // ssh-agent を WSL2 や devcontainer で使うための回避策
  "remote.SSH.enableAgentForwarding": true,
  "remote.SSH.useLocalServer": false,                             // Window 間で別々の接続にする
  "remote.SSH.useExecServer": true,                               // 従来の接続方式を利用する
  "remote.SSH.remoteServerListenOnSocket": true,                  // Socket Listen を有効化
  "terminal.integrated.persistentSessionReviveProcess": "never",  // ターミナルの復元、再作成を行わない.
  "terminal.integrated.enablePersistentSessions": false           // ターミナルセッション履歴を保持しない
}

remote*, と terminal* は WSL2 上で ssh-agent を使う場合うまく SOCK を引き継げない事象になることがあるので追記している。

Troubleshooting

Yubikey の OpenGPG 鍵認識

一番鍵の状態、keygrip、鍵の用途が確認できるコマンドは gpg-card -q list おそらくこれ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
PS C:\Users\naa0yama> gpg-card -q list
Reader ...........: Yubico YubiKey OTP FIDO CCID 0
Card type ........: yubikey
Card firmware ....: 
Serial number ....: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Application type .: OpenPGP
Version ..........: 
Displayed s/n ....: 
Manufacturer .....: Yubico (6)
Name of cardholder: Aoyama Naoki
Language prefs ...: ja
Salutation .......: Mr.
URL of public key : https://keybase.io/naa0yama/pgp_keys.asc
Login data .......: Aoyama Naoki
Signature PIN ....: 強制
Max. PIN lengths .: 127 127 127
PIN retry counter : 4 99 10
Signature counter : 5
Capabilities .....: key-import algo-change button priv-data
KDF setting ......: on
UIF setting ......: Sign=on Decrypt=on Auth=off
Signature key ....: CBXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      keyref .....: OPENPGP.1  (sign,cert)
      algorithm ..: ed25519
      stored fpr .: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      created ....: 2024-09-13 03:43:22
      used for ...: OpenPGP
        main key .: 65284A0A9205C52EBC16BBCF794676DEF45A4D7F
        fpr ......: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        created ..: 2024-09-13 03:43:22
        user id ..: Naoki Aoyama <9667078+naa0yama@users.noreply.github.com>
Encryption key....: 65XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      keyref .....: OPENPGP.2  (encr)
      algorithm ..: cv25519
      stored fpr .: 4EXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      created ....: 2024-09-13 03:14:12
      used for ...: OpenPGP
        main key .: 65284A0A9205C52EBC16BBCF794676DEF45A4D7F
        fpr ......: 4EXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        created ..: 2024-09-13 03:14:12
        user id ..: Naoki Aoyama <9667078+naa0yama@users.noreply.github.com>
Authentication key: F3XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      keyref .....: OPENPGP.3  (sign,auth)
      algorithm ..: ed25519
      stored fpr .: 18XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      created ....: 2024-09-13 03:44:07
      used for ...: OpenPGP
        main key .: 65284A0A9205C52EBC16BBCF794676DEF45A4D7F
        fpr ......: 18XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        created ..: 2024-09-13 03:44:07
        user id ..: Naoki Aoyama <9667078+naa0yama@users.noreply.github.com>

gpg-agent 経由で使えない鍵がある

gpg-agent に頻繁に違う種類の鍵を登録したりしている場合は、鍵のリストがいっぱいになり 認証試行の上限を超えてしまうことがある。 gpg-agent は登録された鍵の fingerprint を $env:APPDATA/gnupg/sshcontrol に登録するため確認して一度全ての登録を削除すると認証できる可能性がある。

Debug で確認するコピペ
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
ssh.exe -V


gpg-agent --version


winget list | Select-String -Pattern gpg


cat $env:APPDATA/gnupg/gpg-agent.conf


cat $env:APPDATA/gnupg/scdaemon.conf


cat $env:APPDATA/gnupg/sshcontrol

Windows で Ed25519

Windows で Ed25519 を生成するメモ

pwsh.exe
1
2
3
New-Item "$env:USERPROFILE\.ssh" -ItemType Directory -Force
ssh-keygen.exe -t ed25519 -f $env:USERPROFILE\.ssh\$env:USERNAME@$Env:COMPUTERNAME -C $env:USERNAME@$Env:COMPUTERNAME
cat $env:USERPROFILE\.ssh\$env:USERNAME@$Env:COMPUTERNAME
Hugo で構築されています。
テーマ StackJimmy によって設計されています。