惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
C
Check Point Blog
J
Java Code Geeks
腾讯CDC
Martin Fowler
Martin Fowler
MongoDB | Blog
MongoDB | Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - 三生石上(FineUI控件)
Apple Machine Learning Research
Apple Machine Learning Research
大猫的无限游戏
大猫的无限游戏
Engineering at Meta
Engineering at Meta
罗磊的独立博客
Last Week in AI
Last Week in AI
B
Blog
IT之家
IT之家
S
SegmentFault 最新的问题
D
DataBreaches.Net
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
GbyAI
GbyAI
博客园 - 聂微东
U
Unit 42
有赞技术团队
有赞技术团队
Y
Y Combinator Blog
MyScale Blog
MyScale Blog

Dejavu's Blog

甲骨文 ARM 实例部署 Gemma 4 模型 Headscale + Tailscale 组建虚拟专用网 在 Linux 上使用 Yubikey OpenPGP 应用 BuyVM VPS 块存储挂载教程 Alpine Linux 服务器配置指南 Alpine Linux 安装 Cloudflared Docker 多容器共享中心数据库 安装 Komari 服务器监控工具 Scaleway VPS 安装 Debian Linux Debian 13 下部署 AsmBB 论坛 使用 Kopia 自动化备份服务器数据 给 Docker 启用 IPv6 支持 Netcup 服务器安装自定义 ISO 镜像 烽火 HG5582A 光猫开启桥接模式 Docker 自托管 Shlink 短链服务 部署 Obsidian LiveSync 实时同步服务指南 我的 2025 年不完全回顾 Linux 下 Intel 核显驱动配置与硬件加速 Fedora Linux 安装配置记录 2025 年优雅地自托管 RSS 服务 Woodpecker CI 和 Gitea 实现 Hugo 自动部署 Gitea/Forgejo 集成 Woodpecker CI/CD 在 Blinko 中使用 Ollama 作为 AI 供应商 Docker 部署 Gitea/Forgejo Plausible CE 启用城市级地理位置识别 Blinko 开源 AI 知识库 Docker 部署指南 Netcup 免税账号注册及购买服务器全记录 Docker 自托管 Cloudreve Pro 私有网盘服务 GiffGaff SIM 卡使用体验和注意事项 简体中文互联网在变得糟糕吗?
在 Windows 中使用 Yubikey 的 OpenPGP 应用
Dejavu Moe · 2025-10-23 · via Dejavu's Blog

前言

几年前,我开始接触硬件安全密钥,当时使用的 Canokey 它实现了 Yubikey 的绝大部分功能,在 Canokey 签名和认证应用指南 中记录了我的配置过程(该指南同样适用于 Yubikey)。

然而,时隔数年,当我尝试在 Windows 系统上重新配置此流程时,却遭遇了诸多令人困惑的地方。难以排查系统内建的 Win32-OpenSSH 与通过 Scoop 安装的 GnuPG (而非 Gpg4Win )之间,对于 ssh-agentgpg-agent,如何实现管道和代理转发的复杂机制,它们对我来说就像是个「黑盒子」。

confused.webp

经过查阅 很多 一些资料,我重新完成了这个配置过程,本文仍可能存在错误之处,但其核心价值在于记录,确保下次重装系统后我可以快速完成设置和使用。

本文着力于 Win32-OpenSSH 原生 SSH 客户端 的配置,而非 PuTTY、Cygwin、MSYS2 或 WSL2 中的实现。使用 PuTTY 进行代理转发的方案不够简洁高效,我更喜欢简单、原生的实现和处理。此外, win-gpg-agent 项目的维护者已于 2022 年 9 月宣布停止维护,安全考量可能是其中一个原因。

仔细阅读 GnuPG 清单 T3883 和 GitHub #827 上关于此问题的追踪,可以发现跨系统代理配置困难的主要原因可能在于 Windows 的命名管道 (Named Pipes) 与 *nix 系统的套接字 (Socket) 处理机制存在本质区别,最新的 Win32-OpenSSH 是否已经实现了这个原生命名管道的处理呢?我无从了解,但是现在最起码它正常工作了!

pinentry-basic.webp

本文中,我们的处理流程大致如下:

+----------------+
|    YubiKey     |
|  OpenPGP Keys  |
+-------+--------+
        |
        | (GPG Keys)
        v
+------------------------+
|      gpg-agent         |
| (GPG Agent & SSH Proxy)|
+---+--------+--------+--+
    ^        |        ^
    |        |        | (SSH Auth Request)
    |        |        +---------------------+
    |        |                              |
    | (GPG Sign Request)                    v
+---+-----------------------+        +------+-------------------------+
|   GnuPG / Git (Commit -S) |        | Win32-OpenSSH / Git (Push/SSH) |
+---------------------------+        +--------------------------------+

             (PIN/Touch Authentication)
             <------------------------>

环境

先决条件

我们假定 YubiKey 上的 OpenPGP 应用中已生成并写入了 GPG 密钥。同时默认你此前在其他计算机上的相关配置能够正常工作。在新安装的 Windows 系统上,你已通过 Scoop 完成了 gnupggitsudo 的安装。

scoop install git gnupg
scoop install sudo -g

系统信息

systeminfo | findstr /B /C:"OS 名称" /C:"OS 版本"
OS 名称:            Microsoft Windows 11 IoT 企业版 LTSC
OS 版本:            10.0.26100 N/A Build 26100

GnuPG 信息

GnuPG 的版本信息

gpg --version
gpg (GnuPG) 2.5.16
libgcrypt 1.11.2
Copyright (C) 2025 g10 Code GmbH
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: C:\Scoop\apps\gnupg\current\home
Supported algorithms:
Pubkey: RSA, Kyber, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2

配置文件路径,我的在 C 盘的 Scoop 目录下

gpgconf --list-dirs
sysconfdir:C%3a\Scoop\apps\gnupg\current\etc\gnupg
bindir:C%3a\Scoop\apps\gnupg\current\bin
libexecdir:C%3a\Scoop\apps\gnupg\current\bin
libdir:C%3a\Scoop\apps\gnupg\current\lib\gnupg
datadir:C%3a\Scoop\apps\gnupg\current\share\gnupg
localedir:C%3a\Scoop\apps\gnupg\current\share\locale
socketdir:C%3a\Scoop\apps\gnupg\current\gnupg
dirmngr-socket:C%3a\Scoop\apps\gnupg\current\gnupg\S.dirmngr
keyboxd-socket:C%3a\Scoop\apps\gnupg\current\gnupg\S.keyboxd
agent-ssh-socket:C%3a\Scoop\apps\gnupg\current\gnupg\S.gpg-agent.ssh
agent-extra-socket:C%3a\Scoop\apps\gnupg\current\gnupg\S.gpg-agent.extra
agent-browser-socket:C%3a\Scoop\apps\gnupg\current\gnupg\S.gpg-agent.browser
agent-socket:C%3a\Scoop\apps\gnupg\current\gnupg\S.gpg-agent
homedir:C%3a\Scoop\apps\gnupg\current\home

OpenSSH 信息

使用的是 Windows 默认的 Win32-OpenSSH 客户端

ssh -V
OpenSSH_for_Windows_9.5p2, LibreSSL 3.8.2

配置流程

禁用 SSH Agent 服务

现在的 Win32-OpenSSH 已经有 ssh-agent 服务了,但是它会干扰 gpg-agent 的处理进程,确保它已停止运行

sudo Get-Service ssh-agent

Status   Name               DisplayName
------   ----               -----------
Stopped  ssh-agent          OpenSSH Authentication Agent

否则请停止其服务并禁止自动运行

sudo Stop-Service ssh-agent
sudo Set-Service -StartupType Disabled ssh-agent

识别智能卡

这一步并非强制要求,但根据 YubiKey-Guide 中的 说明 ,现在 Windows 中可能存在一些虚拟智能卡(例如 Windows Hello 相关的读卡器)。有可能干扰 到 scdaemon 的工作,导致它无法正确读取 YubiKey 智能卡设备。

查看设备信息:

Get-PnpDevice -Class SoftwareDevice | Where-Object {$_.FriendlyName -like "*YubiKey*"} | Select-Object -ExpandProperty FriendlyName

会输出 Yubikey 完整的设备名称,比如:

Yubico YubiKey OTP+FIDO+CCID 0

C:\Scoop\apps\gnupg\current\home 下新建一个 scdaemon.conf 写入以下内容:

reader-port Yubico YubiKey OTP+FIDO+CCID 0

插入 Yubikey,查看智能卡状态:

# reload gpg config
gpgconf --launch gpg-agent

# check gpg card
gpg --card-status

导入公钥

从服务器或者本地文件导入我们的 PGP 公钥,我在公钥服务器导入:

curl -fsSL https://pgp.dejavu.moe/ | gpg --import -

信任自己的公钥:

gpg --expert --edit-key [your email/keyid/fingerprint]

gpg> trust

Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)

  1 = I don't know or won't say
  2 = I do NOT trust
  3 = I trust marginally
  4 = I trust fully
  5 = I trust ultimately
  m = back to the main menu

Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y

配置 Git 签名

查看用于签名子密钥的 [KeyID of signature subkey]

gpg --list-keys --keyid-format LONG
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
\Scoop\apps\gnupg\current\home\pubring.kbx
--------------------------------------------
pub   ed25519/[Master KeyID] 2021-05-31 [C]
      [Master Key Fingerprint]
uid                 [ultimate] Dejavu Moe <admin@dejavu.moe>
sub   ed25519/[KeyID of signature subkey]      2022-09-26 [S] [expires: 2035-09-23]
sub   cv25519/[KeyID of encryption subkey]     2021-07-18 [E] [expires: 2035-07-15]
sub   ed25519/[KeyID of authentication subkey] 2022-01-09 [A] [expires: 2035-01-06]

添加 Git 设置:

git config --global user.name "YourName"
git config --global user.email "[email protected]"
git config --global user.signingkey "[KeyID of signature subkey]"
git config --global commit.gpgsign true
git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"

配置 GPG 程序路径:

# find gpg.exe
which gpg.exe
C:\Scoop\apps\gnupg\current\bin\gpg.exe

# then
git config --global gpg.program "C:/Scoop/apps/gnupg/current/bin/gpg.exe"

验证 GPG 签名

新建一个测试文本文件

echo "This is a test message to be signed." > test_sign.txt

尝试签名

gpg --local-user [KeyID/Email] --detach-sign test_sign.txt

验证签名

gpg --verify test_sign.txt.sig test_sign.txt
gpg: Signature made 10/23/25 12:31:54
gpg:                using EDDSA key 2E21425AC287228857E027D5223E2D2495F24232
gpg:                issuer "[email protected]"
gpg: Good signature from "Dejavu Moe <[email protected]>" [ultimate]

配置 SSH 代理

查看用于身份认证子密钥的 keygrip,进入 C:\Scoop\apps\gnupg\current\home 目录,将 [keygrip for authentication subkey] 的内容写入 sshcontrol 文件:

# list all keygrip
gpg -K --with-keygrip
ssb>  ed25519 2022-01-09 [A] [expires: 2035-01-06]
      Keygrip = [keygrip for authentication subkey]

同样在这个目录下写入 gpg-agent.conf 文件:

# enable SSH agent
enable-ssh-support
# enable optimized support for Win32-OpenSSH client
enable-win32-openssh-support
# set the default cache time for PIN codes (10 minutes)
default-cache-ttl 600
# set the maximum cache time for PIN codes (2 hours)
max-cache-ttl 7200
# pinentry dir
pinentry-program C:/Scoop/apps/gnupg/current/bin/pinentry-basic.exe

重新启动相关进程:

gpg-connect-agent killagent /bye
gpg-connect-agent /bye
gpgconf --launch gpg-agent

尝试导出 SSH 公钥:

应该会得到正确的 SSH 公钥格式,将其添加到 GitHub 或 VPS 上的信任 SSH 公钥列表里,比如:

ssh-ed25519 AAAA.../... cardno:12_345_987

验证 GPG SSH

测试下 GitHub SSH 访问

应该没问题了

Hi DejavuMoe! debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
You've successfully authenticated, but GitHub does not provide shell access.

最后

还是想吐槽下这个 pinentry-basic.exe,每次输入 PIN 解锁 Yubikey 时,都要用鼠标去输入框点一下,它无法自动获取输入焦点,暂时只能做到在 GPG 签名时可以直接在终端里输入 PIN。

pinentry-tty-sign.webp

嗯,现在一切就已经完成了。然而,目前对于 SSH 认证的 PIN 输入问题仍未找到满意的解决方案,类似 pinentry-tty 的思路在当前环境下(指 Windows 原生环境)尚未成功实现,算是一个小痛点吧。

参考资料: