Back
Featured image of post YubiKey 入手记 - GPG

YubiKey 入手记 - GPG

我的 GPG 公钥:https://github.com/ichenhe.gpg

背景介绍

YubiKey 是一个 著名 很贵的硬件密钥,本次到手的是 YubiKey 5c NFC,不着急的朋友建议等等国人的一个开源项目 CanoKeys,实现了和 YubiKey 5 几乎一致的功能,但便宜很多。不过从用户反应来看,Yubico 似乎性能更强一些,签名时速度较快。

YubiKey 支持的协议很多,这次我们仅关注 GPG 方面。官方的 GUI 程序并不提供 GPG 的可视化配置(也许用到 GPG 的人都更喜欢命令行吧)。

什么是 GPG

首先得搞清楚一个和它很像的东西:PGP (Pretty Good Privacy),这是一个非对称加密协议,可惜它本身是商业软件。于是万能的开源社区搞出了一个遵循此标准的免费实现:GPG (Gnu PG),也就是我们现在可以随意使用的东西啦。在本文中,PGP 与 GPG 是等价的。

能力(用途)

GPG 有许多能力,见下表:

缩写 能力 备注
[C] Certificating 认证其他密钥,比如签署一个子密钥。
[S] Signing 签名。证明某数据没有被篡改。例如文件签名,git commit 签名。
[A] Authenticating 认证。比如 SSH 登录。
[E] Encrypting 加密。

子密钥

GPG 设计之初就包含子密钥的概念。其实实质上,子密钥和主密钥没有什么区别,只不过 GPG 默认做了包装:

  1. 子密钥由主密钥签名,继承了主密钥的身份。
  2. 子密钥的公钥包含在主密钥里,无需单独分发。
  3. 子密钥与主密钥共享吊销证书,无需单独维护。

有没有感觉和 HTTPS 证书链很像?没错,就是这样!只是 GPG 是去中心化的,没有 CA,并且由于上述包装,客户端可以一次性验证子密钥,而不需要一层层请求。

没有 CA?

是的,没有 CA。也许这就注定了 GPG 小众的结局。

没有 CA 意味着我们必须手动分发公钥,例如 Github 要求我们亲自上传公钥。也有人会把公钥放在自己的博客来 提高B格 供有需要的朋友验证签名或发送加密消息。

现在你知道了,分发公钥很麻烦。因此出现了 Key Server,但它不是 CA! 可以把 Key Server 想象成网盘,大家都把公钥传上去,然后只需相对简短的 id 或者 用户 id 就可以轻松下载导入了。但是 Key Server 不会对用户做认证,也就是说任何人都可以声称自己是任何人。 因此,不要轻易信任服务器上的公钥。

除此之外,Key Server 被设计成无法删除数据——你可以添加信息,可以吊销证书,但是不能删除它。这样做的目的是尽可能避免行政手段的干预。

发布到 CA 很简单,执行 gpg --keyserver keys.openpgp.org --send-keys <id>。Key Server 有几个著名的,他们之间会定时同步。根据需要上传就行。

⚠️ 注意,上传到 Key Server 的公钥是完全公开的,包括内置的名字、电子邮箱等,注意隐私问题。一旦上传则无法删除。

创建 GPG

创建主密钥

知道了什么是 GPG,现在可以着手创建一个密钥了。默认已经装好了 gpg 工具。

创建密钥非常简单,只需找一台安全的电脑,而不是充满了木马与流氓软件(比如 Qx)的设备,然后执行:

gpg --gen-key

跟着引导一步步来就行。截止目前(2022.1)默认已经使用 ECC 而不是 RSA 了。相同强度下 ECC 密钥更小,效率更高。如果有兼容老设备等需求,可以执行

gpg --full-generate-key

来自定义算法。

创建子密钥

从安全角度,主密钥应当仅用于签署子密钥,创建后应该离线妥善保存。而日常工作则使用子密钥。

但是注意,Maven central 要求必须用主密钥签署才能发布

执行 gpg --list-keys(缩写 gpg -k)来列出刚刚创建的密钥。截止目前,gpg 工具会默认生成一个用于加密 ([E]) 的子钥。

> gpg --list-keys
/Users/.../.gnupg/pubring.kbx
--------------------------------
pub   ed25519 2022-01-10 [SC] [有效至:2024-01-10]
      092CF9F4407E1C8D6F2734997D9FDEBFD006FAE0  # <----- 这个就是 id
uid             [ 绝对 ] Chenhe <liangchenhe55@gmail.com>
sub   cv25519 2022-01-10 [E] [有效至:2024-01-10]

要创建新的子钥,得执行 gpg --expert --edit-key <id> 来编辑主密钥,在编辑模式下执行 addkey 即可根据引导添加子密钥。需要注意的是,用于认证 ([A]) 的密钥不是很常用,因此默认没有这个选项,需要选择「自定义用途」来手动指定。

添加完成后在编辑模式,执行 q 退出,会询问是否保存,选择 Y。注意如果这时候按 ctrl+c 强退了就不会保存刚才的修改。

备份

在把密钥添加到 YubiKey 之前,先导出备份。因为这是「移动」而不是「复制」,同时 YubiKey 不允许导出内部密钥。执行

gpg -o /path/to/save/my.gpg --export-secret-keys <id>

新的 gpg 工具,只需导出主密钥的私钥,即可自动导出主+子密钥的公钥+私钥。

添加 --armor 参数可以导出文本格式。

吊销

以防万一我们私钥泄漏,还有必要创建并备份一个吊销证书。吊销证书一旦生成,可以在没有私钥、无需密码的情况下 声明一个私钥作废。因此吊销证书也应该安全保管,虽然泄漏不会导致数据被破解,但会带来很多麻烦。执行:

gpg --output /path/to/save/revoke.asc --gen-revoke <id>

可以创建并导出一个吊销证书。


等那一天真的来临(无论是私钥泄漏,还是整个密钥不打算继续使用了),只需要简单地导入这个特殊的密钥,就可以作废之:

# 声明作废,不要轻易执行
gpg --import /path/to/save/revoke.asc

现在,你需要把吊销后的密钥(不是吊销密钥本身)重新分发,例如上传到 Key Server,让其他人知道这个公钥已经不可信了。

添加到 YubiKey

现在插入小钥匙🔑,执行 gpg --card-status 应该可以读取到 YubiKey 的 GPG 状态。读取不到的话请检查连接。

> gpg --card-status
Reader ...........: Yubico YubiKey OTP FIDO CCID
Application ID ...: X000000000000000000000000000000
Application type .: OpenPGP
Version ..........: 0.0
Manufacturer .....: Yubico
# ......

添加密钥

再次编辑主密钥 gpg --expert --edit-key <id>,默认选中的是主密钥 (0),可以执行 key <x> 来选择子密钥,x 为顺序号 (0, 1, 2…)。某密钥被选中后,前面会出现 * 的标志,但是主密钥除外。

gpg> key 1 # 选择第一个子密钥

sec  ed25519/7D9FDEBFD006FAE0
     创建于:2022-01-10  有效至:2024-01-10  可用于:SC
     信任度:绝对        有效性:绝对
ssb* cv25519/ED5F951C04BF0D1C
     创建于:2022-01-10  有效至:2024-01-10  可用于:E

选中后执行 keytocard 就可以导入 YubiKey 啦,根据密钥的用途选择对应的存储槽就好。

最后依然是执行 q 保存退出。

⚠️ 再次警告,导入后本地私钥会被删除,务必先导出到文件备份。

到此为止已经导入完成了。为了安全,可以执行 gpg --delete-secret-keys <id> 删除电脑上的私钥,然后把备份文件加密离线脱机保存。现在,除非插上 YubiKey 否则无法进行签名/认证/加解密。

配置 YK

首次使用应该先修改一下 YubiKey 的默认 PIN,以免被其他人盗取后可以签名。

执行 gpg --edit-card 进入智能卡编辑模式,然后执行 admin 进入管理员模式。执行 help 可以列出允许的操作,有许多字段可以编辑。我们这边执行 passwd 根据提示修改 PIN(调用私钥时验证)和 Admin PIN(修改智能卡属性时使用)。前者默认密码是 123456,后者是 12345678

⚠️ 注意,GPG 数据(包括 PIN)和 YubiKey PIV 数据相互独立,没有关系。

通过 YubiKey 的工具 ykman 还可以进行更多设置,例如认证时要求触摸铜片,以免后台被恶意程序偷偷签名。

 ykman openpgp keys set-touch [SIG|ENC|AUT|ATT] [on|off|fixed|cached|cached-fixed]

更多详细说明看看官方文档吧。


拥有公钥+私钥,才可以正常进行签名等操作。可惜 YubiKey 本身不保存公钥,这意味如果使用一台新设备,则部分功能可能受限。如果公钥上没有很重要的隐私信息,一个方便的方案是将其传到 Key Server,或者 Gayhub Github。然后将公钥下载地址配置到 YubiKey 里。例如已经把公钥配置到了 Github,那么地址就是 https://github.com/<username>.gpg,执行下面命令将其配置到 YK:

> gpg --edit-card
gpg/card> admin
gpg/card> url https://github.com/<username>.gpg
gpg/card> q

那么在新设备上,就可以执行下面的命令获取并导入公钥:

> gpg --edit-card
gpg/card> fetch

参考