Yubikey 的使用和配置
最近拿到了几个 Yubikey,总结一下在 2022 年我是如何配置 Yubikey 的。
使用 Yubikey OTP 认证
这是一个 Yubikey 最基础的认证方式之一,当 Yubikey 插入到 USB 接口之后触摸金属点会自动输入一个长度为 44 的字符串和一个回车符号,这 44 个字符由 12 位始终一致的编号和 32 位随时间动态改变的编码组成。使用时需要在对应平台预先填写 44 位字符串,在登录时验证新的字符串即可,以 LastPass 为例,最多支持绑定 5 个 Yubikey,在设置里面绑定之后,登录账号时会显示一个一样的文本输入框,触摸金属点会输入密码并带上回车自动登录。Bitwarden/Vaultwarden 也支持这种方式。
Yubikey OTP 的实现原理可以看OTPs Explained,他的缺点是需要依赖 YubiCloud 的服务器进行验证,这带来了可用性的问题,以及隐私和安全性问题。
使用 WebAuthn 认证
之所以先提 WebAuthn 是因为它是一个中立的标准,也是目前体验相对流畅的认证方式,WebAuthn 是一个W3C 标准,目前主流浏览器已经支持,尤其特别值得一提的是在移动端比如 iOS Safari 和 Android Chrome 已经完全支持,这样使得真正能够在全平台使用。WebAuthn 不仅仅适配 Yubikey 这样的独立设备,也适配手机的指纹和面部识别,甚至是 Windows Hello 这样的 Windows 系统的认证方式,这样就可以在不同的平台上使用同样的认证方式,而不需要为每个平台单独配置。
在 WebAuthn.io 上可以直接测试 WebAuthn 的支持情况,和大多数认证方式一样,需要先注册,下面是 Chrome 测试的情况,默认不会使用 Yunbikey,需要手动选择 USB 安全密钥。
然后在选择 USB 安全密钥这一项的时候可能会出现两种情况,一种是让你设置 PIN 一种是直接验证通过,这是因为 FIDO2 和 FIDO U2F 的区别:
- FIDO U2F 是 FIDO2 的前身,U2F 仅支持多因素模式,主要设计用来加强用户名/密码验证
- FIDO2 是一个更新的标准,最主要的区别是同时支持多因素和单因素模式,这里的单因素模式就是指不需要用户名和密码的情况直接使用 FIDO2 进行单一因素认证,实现所谓无密码登录。在单因素模式下 Yubikey 需要 PIN 码进行验证,虽然文档一直在强调 Passwordless 中却需要用 PIN 码来保证不会有人捡到 Yubikey 之后直接登录账号
- 一般定义 FIDO2 是 WebAuthn + CTAP2 构成
WebAuthn 保持了对旧版 FIDO U2F 的兼容性,参考这里,目前测试发现 Google 账号、Github 账号、Cloudflare 账号都是使用的 FIDO U2F 作为二次认证,而不是 FIDO2,但是使用的是 WebAuthn 的接口。WebAuthn 在移动端也有良好的兼容性,测试发现如果在 PC 浏览器通过 USB 方式给 Google 账号绑定 Yubikey,在 iOS Safari 上会提示使用 NFC 方式进行认证。下图是 Cloudflare 账号在 PC 的登录界面。
参考资料:
- 关于 FIDO 联盟 https://fidoalliance.org/overview/?lang=zh-hans
- WebAuthn Introduction https://developers.yubico.com/WebAuthn/
- What is FIDO 2? https://www.yubico.com/resources/glossary/fido-2/
- Web Authentication API https://caniuse.com/?search=webauthn
- 使用安全密钥 (WebAuthn) 进行双重身份验证 https://developers.google.com/codelabs/webauthn-2fa-key
- Developments to WebAuthn and the FIDO2 Framework https://duo.com/blog/developments-to-webauthn-and-the-fido2-framework
- 谈谈 WebAuthn https://flyhigher.top/develop/2160.html
- Client to Authenticator Protocol (CTAP) https://fidoalliance.org/specs/fido-v2.0-id-20180227/
使用 OATH(TOTP) 认证
TOTP(Time-Based One-Time Password) 基于时间的一次性密码 (RFC6238),可能是除短信外最常见的多因素验证方式,常见的使用方式是直接使用 Google Authenticator、Microsoft Authenticator 扫描网站提供的二维码,下次需要登录的时候查看 Authenticator 生成的 6 位密码,密码每隔 30 秒发生变化,这种验证方式非常普遍和常用。
Yubico Authenticator 的使用方式稍微有点不同,区别在于使用 Yubico Authenticator 之后需要通过 NFC 或者其他方式连接 Yubikey,然后在 Yubico Authenticator 中添加账号,账号信息直接保存在 Yubikey 中,下次需要查看的时候需要继续连接 NFC 或者其他方式。根据其 FAQ 的描述,一个 Yubikey 最多支持存储 32 个 TOTP 账号。
其实大多数场合直接使用 Google Authenticator、Microsoft Authenticator 这样的 App 足够满足安全性和便捷性的要求,Yubico Authenticator 的使用方式更加复杂,但是存储在 Yubikey 相比 App 可能更加安全,同时可以避免 App 被清除数据或者手机被破坏导致的数据丢失。一般来说建议使用 Google Authenticator、Microsoft Authenticator 之类的工具或者 Yubico Authenticator 都需要注意备份:
- 添加 OTP 的时候可以通过多个手机同时扫描二维码的方式进行备份,每个手机都会显示一样的密码。如果是已经添加过的账号,可以通过 Google Authenticator 导入导出备份。Google Authenticator 导出时会生成多张二维码给另一台手机扫描,似乎备份这几张二维码也可行?
- 可以直接备份绑定时的二维码或者其对应文本,这样手机丢失可以重复扫描二维码进行恢复,但是这样需要保证信息妥善存储防止泄露,一些密码管理工具甚至集成了类似功能,但是将密码和 OTP 放一起不是很合理
- 同样的,一般建议使用 Yubikey 的时候同步使用一个备份 Yubikey 存储统一的信息,防止 Yubikey 丢失或者损坏
参考资料:
- https://www.yubico.com/resources/glossary/oath-totp/
- https://docs.yubico.com/yesdk/users-manual/application-oath/uri-string-format.html
- https://ruimarinho.gitbooks.io/yubikey-handbook/content/oath/yubico-authenticator.html
使用 FIDO U2F 认证 SSH
OpenSSH 从 8.2 开始支持 FIDO/U2F,可以使用 Yubikey 作为 SSH 的二次认证,这样可以避免使用密码登录 SSH,同时也可以避免使用 SSH Key 登录的时候需要输入密码的问题。需要注意的是 OpenSSH 8.2 发布于 2020-02-14,意味着之前很多发行版不支持。
注意:macOS 内置的 openssh(路径一般为 /usr/bin/ssh
,以下简称为 macos/openssh) 不支持 FIDO/U2F,所以需要通过 brew 安装最新版的 openssh(路径一般为 /opt/homebrew/bin/ssh
,以下简称 brew/openssh),但是 brew/openssh 不支持 Keychain 这意味着如果 ssh key 设置了密码,每次使用或者添加到 ssh-agent 时都需要输入密码。
brew install openssh
然后通过 ssh-keygen 生成 ~/.ssh/id_ed25519_sk
ssh-keygen -t ed25519-sk -C name@example.com
# 其他方式和参数建议阅读 https://aditsachde.com/posts/yubikey-ssh/
生成之后更新 vim ~/.ssh/config
让 github 使用这个文件,使用 IgnoreUnknown UseKeychain
可以让 brew/openssh 不报错使得这份配置文件同时兼容 macos/openssh,不过并不能让 brew/openssh 使用 Keychain。
Host github.com
IgnoreUnknown UseKeychain
UseKeychain yes
HostName github.com
User git
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519_sk
通过 ssh-add
添加到 ssh-agent
eval `ssh-agent`
ssh-add ~/.ssh/id_ed25519_sk
ssh-add -l
添加完成之后可以进行 git 操作测试或者使用 ssh -T
测试是否正常工作,注意验证 key 的时候可能屏幕不会提示但是 Yubikey 会有灯光闪烁提示,闪烁时触摸即可认证。
ssh -T git@github.com
总之目前(2022 年)直接使用 FIDO U2F 认证 SSH 的方式还不太成熟,主要问题是 macOS 兼容问题,以及目标服务器 openssh 的支持问题。但是如果不使用 macOS 和较旧的服务器,这种方式是非常好用的。
参考资料:
- Using a Yubikey for SSH on macOS https://aditsachde.com/posts/yubikey-ssh/
- https://stackoverflow.com/questions/47455300/ssh-config-bad-configuration-option-usekeychain-on-mac-os-sierra-10-12-6
- https://developer.apple.com/library/archive/technotes/tn2449/_index.html
- https://developers.yubico.com/SSH/
- https://github.com/vorburger/vorburger.ch-Notes/blob/develop/security/ed25519-sk.md
使用 GPG
备份和配置
使用 Yubikey 可以直接存储 GPG 私钥,甚至可以直接在 Yubikey 内生成私钥,但是出于备份和迁移考虑一般不会这么干,因为存储在 Yubikey 的 GPG 私钥无法被读取,但是可能被重置。即使是在外部生成的 GPG 私钥,在存储到 Yubikey 前一定要注意备份,因为存储到卡片的过程是直接将私钥转移到卡片。下面的过程主要是导入现有的 key 到 Yubikey 内。操作前可以使用 GUI 工具或者运行以下命令导出 key 进行备份(备份 1),1234ABC
为对应 key ID。
gpg --export-secret-key --armor 1234ABC
查看卡片状态
gpg --card-status
输出如下,其中 Signature key
Encryption key
Authentication key
分别表示 Yubikey 支持的签名、加密、认证三个证书,下面的状态表示此 Yubikey 尚未存储任何 Key。
Reader ...........: Yubico YubiKey OTP FIDO CCID
Application ID ...: ******
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: ******
Name of cardholder: [未设定]
Language prefs ...: [未设定]
Salutation .......:
URL of public key : [未设定]
Login data .......: [未设定]
Signature PIN ....: 非强制
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
KDF setting ......: off
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]
然后查看系统已导入的 key,找到对应 key 的 ID,下面为 123ABC
gpg --list-secret-keys
/Users/phyng/.gnupg/pubring.kbx
-------------------------------
sec rsa4096 2018-11-20 [SC]
123ABC***
uid [ 绝对 ] *** (***) <***@gmail.com>
ssb rsa4096 2018-11-20 [E]
ssb rsa4096 2022-12-19 [A]
创建子密钥
因为 Yubikey 5(C) NFC 只支持存储 3 个 GPG 私钥,这里将主密钥用于 SC 即签名和认证功能,然后创建两个子密钥分别用于加密和鉴权。可以按照下面的方法添加子密钥。
- 输入
gpg --expert --edit-key ***
进入证书编辑 - 输入
addkey
命令增加 key,选择8
为 RSA 自定义用途 - 输入
S
和E
分别关闭签名和加密功能,选择A
开启鉴权功能 - 输入
Q
退出功能编辑 - 密钥长度选择
4096
- 有效期选择
0
表示无限 - 输入
quit
保存退出 - 添加子私钥后会更新公钥,可以重新上传到公共服务器
证书配置好后,可以导出备份(备份 2)这样后续操作失败可以无限恢复,然后再写入到 Yubikey。
写入到 Yubikey
使用下面编辑对应 key,将 123ABC
替换为对应 key 的 ID,再次强调迁移私钥前前请保证当前私钥已经妥善备份并且测试过备份可以正常恢复使用!!!
gpg --expert --edit-key 123ABC***
进入编辑界面之后,分步进行以下操作,注意操作过程可能会问 GPG 私钥设置的密码,以及 Yubikey GPG 管理 PIN,默认为 12345678
,需要注意的是设置好之后使用卡的时候会询问用户 PIN ,默认为 123456
,如果输错次数超过设定次数会导致锁定。下面的操作将主密钥和 2 个子密钥分别存储到了 Yubikey 对应位置。
- 输入
keytocard
,选择y
,选择1
,将主密钥存储到签名密钥位置 - 输入
key 1
选择子密钥 key 1 ,输入keytocard
,选择2
,将子密钥存储到加密密钥位置 - 输入
key 1
取消选择 key 1,再 输入key 2
选择 key 2 ,输入keytocard
,选择3
,将子密钥存储到身份验证位置 - 输入
quit
,选择y
,保存退出
下面是执行过程记录
gpg --expert --edit-key 123ABC***
gpg (GnuPG/MacGPG2) 2.2.40; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
私钥可用。
sec rsa4096/31***
创建于:2018-11-20 有效至:永不 可用于:SC
信任度:绝对 有效性:绝对
ssb rsa4096/A9***
创建于:2018-11-20 有效至:永不 可用于:E
ssb rsa4096/FA***
创建于:2022-12-19 有效至:永不 可用于:A
[ 绝对 ] (1). *** (***) <***@gmail.com>
gpg> keytocard
真的要移动主密钥吗?(y/N) y
请选择在哪里存储密钥:
(1) 签名密钥
(3) 身份验证密钥
您的选择是? 1
sec rsa4096/31***
创建于:2018-11-20 有效至:永不 可用于:SC
信任度:绝对 有效性:绝对
ssb rsa4096/A9***
创建于:2018-11-20 有效至:永不 可用于:E
ssb rsa4096/FA***
创建于:2022-12-19 有效至:永不 可用于:A
[ 绝对 ] (1). *** (***) <***@gmail.com>
gpg> key 1
sec rsa4096/31***
创建于:2018-11-20 有效至:永不 可用于:SC
信任度:绝对 有效性:绝对
ssb* rsa4096/A9***
创建于:2018-11-20 有效至:永不 可用于:E
ssb rsa4096/FA***
创建于:2022-12-19 有效至:永不 可用于:A
[ 绝对 ] (1). *** (***) <***@gmail.com>
gpg> keytocard
请选择在哪里存储密钥:
(2) 加密密钥
您的选择是? 2
sec rsa4096/31***
创建于:2018-11-20 有效至:永不 可用于:SC
卡号: A***
信任度:绝对 有效性:绝对
ssb* rsa4096/A9***
创建于:2018-11-20 有效至:永不 可用于:E
ssb rsa4096/FA***
创建于:2022-12-19 有效至:永不 可用于:A
[ 绝对 ] (1). *** (***) <***@gmail.com>
gpg> key 2
sec rsa4096/31***
创建于:2018-11-20 有效至:永不 可用于:SC
卡号: A***
信任度:绝对 有效性:绝对
ssb* rsa4096/A9***
创建于:2018-11-20 有效至:永不 可用于:E
ssb* rsa4096/FA***
创建于:2022-12-19 有效至:永不 可用于:A
[ 绝对 ] (1). *** (***) <***@gmail.com>
gpg> key 1
sec rsa4096/31***
创建于:2018-11-20 有效至:永不 可用于:SC
卡号: A***
信任度:绝对 有效性:绝对
ssb rsa4096/A9***
创建于:2018-11-20 有效至:永不 可用于:E
ssb* rsa4096/FA***
创建于:2022-12-19 有效至:永不 可用于:A
[ 绝对 ] (1). *** (***) <***@gmail.com>
gpg> keytocard
请选择在哪里存储密钥:
(3) 身份验证密钥
您的选择是? 3
sec rsa4096/31***
创建于:2018-11-20 有效至:永不 可用于:SC
卡号: A***
信任度:绝对 有效性:绝对
ssb rsa4096/A9***
创建于:2018-11-20 有效至:永不 可用于:E
卡号: A***
ssb* rsa4096/FA***
创建于:2022-12-19 有效至:永不 可用于:A
[ 绝对 ] (1). *** (***) <***@gmail.com>
gpg> quit
要保存变更吗?(y/N) y
再次使用 gpg --card-status
查看卡的状态,可以看到
Reader ...........: Yubico YubiKey OTP FIDO CCID
Application ID ...: ****
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: ****
Name of cardholder: [未设定]
Language prefs ...: [未设定]
Salutation .......:
URL of public key : [未设定]
Login data .......: [未设定]
Signature PIN ....: 非强制
Key attributes ...: rsa4096 rsa4096 rsa4096
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
KDF setting ......: off
Signature key ....: **** **** **** **** **** **** 31** **** **** ****
created ....: 2018-11-20 03:32:15
Encryption key....: **** **** **** **** **** **** A9** **** **** ****
created ....: 2018-11-20 03:32:15
Authentication key: **** **** **** **** **** **** FA** **** **** ****
created ....: 2022-12-19 00:07:40
General key info..: pub rsa4096/31** 2018-11-20 *** (***) <***@gmail.com>
sec> rsa4096/31*** 创建于:2018-11-20 有效至:永不
卡号: ****
ssb> rsa4096/A9*** 创建于:2018-11-20 有效至:永不
卡号: ****
ssb> rsa4096/FA*** 创建于:2022-12-19 有效至:永不
卡号: ****
使用 ykman
管理卡片
可以通过 ykman
管理卡片比如修改重试次数或者重置 GPG 设置。
# 参考文档 https://docs.yubico.com/software/yubikey/tools/ykman/OpenPGP_Commands.html
# 修改密码重试次数
> ykman openpgp access set-retries 10 10 10
# 查看信息
> ykman openpgp info
OpenPGP version: 3.4
Application version: 5.4.3
PIN tries remaining: 10
Reset code tries remaining: 0
Admin PIN tries remaining: 10
Signature PIN: Always
Touch policies:
Signature key: Off
Encryption key: Off
Authentication key: Off
Attestation key: Off
# 重置 GPG
# 警告:重置 GPG 功能非常危险,无需任何密码即可通过下面的命令抹掉 3 个 GPG key,如果需要重新配置 key 的时候可以使用,如果没有备份私钥重置之后私钥就没有了。
ykman openpgp reset
通过 GPG 命令行修改相关密码,进入编辑界面后输入 admin
进入管理员模式,再输入 help
可以查看全部支持的子命令。
> gpg --card-edit
gpg/card> admin
管理员命令可用
gpg/card> help
quit 退出此菜单
admin 显示管理员命令
help 显示此帮助
list 列出所有可用数据
name 更改卡持有人的姓名
url 更改拉取密钥的 URL
fetch 根据卡中指定的 URL 获取密钥
login 更改登录名
lang 更改语言偏好
salutation 变更卡片持有人的称呼
cafpr 更改一个 CA 指纹
forcesig 切换签名强制使用 PIN 标志
generate 生成新的密钥
passwd 更改或解锁 PIN 的菜单
verify 验证 PIN 并列出所有数据
unblock 使用重置码解锁 PIN
factory-reset 销毁所有密钥和数据
kdf-setup 针对 PIN 身份验证设置 KDF
key-attr 更改密钥属性
gpg/card> passwd
gpg: 检测到 OpenPGP 卡,号码为 ******
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
您的选择是?
正常默认插卡第一次使用时需要输入用户 PIN,后续和使用存在电脑内的证书一样不需要输入 PIN,也可以额外设置每次操作是否需要触摸确认,一般来说没有必要。
配置备份 Yubikey
如果需要配置备份卡,最简单的方式是使用备份 2 恢复到备份卡片,参考按以下步骤(再次强调操作前备份好私钥!!!)
- 按上述过程将 原始 key 转移到 Yubikey A ,此时电脑中存储的密钥被转移到了 Yubikey A 中,只留下一个对卡片 Yubikey A 的引用
- 保证备份用的 Yubikey B 位置未写入 key,如果存在先用前述
ykman
工具重置(重置会删除私钥慎重操作!!!) - 拔出 Yubikey A,删除当前引用了 Yubikey A 的 key(删除前确定存在原始完整备份!这一步也可以备份一下当前 key 为备份 3),重新导入未绑定 Yubikey 的 原始 key 备份(前述备份 2)
- 插入 Yubikey B,重复上述写入到 Yubikey 的过程将原始 key 转移到备份 Yubikey B,此时 key 的密钥引用指向了 Yubikey B
- 拔出备份 Yubikey B,重新插上 Yubikey A,运行命令
gpg-connect-agent "scd serialno" "learn --force" /bye
让 GPG 关联卡片 A(见下面记录) - 如果 Yubikey A 丢失或者损坏,插上 Yubikey B 运行
gpg-connect-agent "scd serialno" "learn --force" /bye
让 GPG 关联卡片 B - 如果有更多 Yubikey 可以重复上述操作
下面是关联操作前后的记录
# 拔掉卡片 B,重新插入卡片 A 时,key 的卡片序列号依然显示为 B
> gpg --list-secret-keys
/Users/phyng/.gnupg/pubring.kbx
-------------------------------
sec> rsa4096 2018-11-20 [SC]
***31***
卡片序列号 = B***
uid [ 绝对 ] *** (***) <***@gmail.com>
ssb> rsa4096 2018-11-20 [E]
ssb> rsa4096 2022-12-19 [A]
# 运行此命令同步到卡片 A
> gpg-connect-agent "scd serialno" "learn --force" /bye
S SERIALNO A***
OK
S PROGRESS learncard k 0 0
S PROGRESS learncard k 0 0
S PROGRESS learncard k 0 0
OK
> gpg --list-secret-keys
/Users/phyng/.gnupg/pubring.kbx
-------------------------------
sec> rsa4096 2018-11-20 [SC]
***31***
卡片序列号 = A***
uid [ 绝对 ] *** (***) <***@gmail.com>
ssb> rsa4096 2018-11-20 [E]
ssb> rsa4096 2022-12-19 [A]
备份和安全性问题
从上述过程可以看出,如果备份 2 存在即使 Yubikey 全部丢失也能正常恢复密钥的使用权,所以可以将备份 2 导出到理想存储或者其他你认为安全的地方。另外需要注意,Yubikey 只保护了存储在其上面的 GPG key 无法被导出,但是可以简单的通过 ykman openpgp reset
无需任何密码重置抹掉 GPG key。
GPG 配置参考资料
- https://support.yubico.com/hc/en-us/articles/360013790259-Using-Your-YubiKey-with-OpenPGP
- https://docs.yubico.com/software/yubikey/tools/ykman/OpenPGP_Commands.html
- https://github.com/drduh/YubiKey-Guide
- https://blog.gimo.me/posts/getting-started-with-yubikey/
- https://security.stackexchange.com/questions/181551/create-backup-yubikey-with-identical-pgp-keys
- https://gist.github.com/loa/1afeea49d60c7de7607d0d241fd489ad
- https://github.com/DataDog/yubikey/blob/master/docs/troubleshooting.md
使用 GPG 认证 SSH
安装依赖
brew install pinentry-mac
编辑 GPG 配置文件
vim ~/.gnupg/gpg-agent.conf
default-cache-ttl 600
max-cache-ttl 7200
pinentry-program /opt/homebrew/bin/pinentry-mac
enable-ssh-support
编辑 ~/.zshrc
和 ~/.bashrc
加入以下内容保证 shell 启动时能够正确加载 gpg-agent
,使用 source ~/.zshrc
刷新或者直接打开新的 shell 测试是否加载成功
export GPG_TTY="$(tty)"
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
gpgconf --launch gpg-agent
修改 SSH 配置文件 vim ~/.ssh/config
取消之前的 IdentityFile
Host *
IgnoreUnknown UseKeychain
UseKeychain yes
AddKeysToAgent yes
# IdentityFile ~/.ssh/id_rsa
UserKnownHostsFile ~/.ssh/known_hosts
使用 ssh-copy-id
复制到远程服务器,测试是否正常
ssh-copy-id user@server.example.com
ssh -v user@server.example.com
或者使用 gpg --export-ssh-key
生成公钥,然后手动复制到远程服务器,在某些场合下可能会更方便
gpg --export-ssh-key 123ABC*** > ~/.ssh/gpg.pub
chmod 600 ~/.ssh/gpg.pub
cat ~/.ssh/gpg.pub
插入备用 Yubikey,通过运行 gpg-connect-agent "scd serialno" "learn --force" /bye
同步后,测试 SSH 联通性即可。
参考资料
- https://serverfault.com/questions/906871/force-the-use-of-a-gpg-key-as-an-ssh-key-for-a-given-server
其他主题
- 使用 PIV 认证 SSH https://github.com/FiloSottile/yubikey-agent
- 使用 Challenge-Response 认证 KeePassXC https://keepassxc.org/docs/