Canokey 应用之 GPG

引言

最近新购入了新版的 Canokey Canary(之前是 Pigeon),抢了个首发版本,也算是对国产的小小支持。

本来早早就想再买个 Canokey 作为双备份,但听闻有 Type-C 版本的要上架,且对 NFC 有改善,因此蹲到了现在。

恰好,既然是作为备份,那么就要重新导入一遍私钥,也借此机会简单记录一下,针对 GPG 签名、认证的一些用法。

尤其是,如何把一个私钥导入到多个 Canokey 中,操作起来有些细节还是挺费事的。

如果对 Bitlocker PIV 的用法感兴趣,可以看我写的前篇

配置硬件

查看卡状态

首先查看硬件,确保能检测到 Canokey:

1
gpg --card-status

输出如下,代表 Canokey 正常连接:

卡片初始化

接下来,对 canokey 进行编辑,输入:

1
gpg --card-edit

进入到交互模式,随后,输入 admin 以允许执行管理命令,如下图:

密码修改

新卡到手第一步,修改 PIN 和 PUK 码。

输入 passwd,得到一个菜单:

分别选择 1、3,修改常用的两个 Pin 码——“用户 PIN” 和 “管理员 PIN”

对于 4,是用来清除卡数据的密码,使用默认即可,我们不用去动它。

完成后按下 Q 退出。

信息设定(可选)

硬件上的 GPG 可以设置持卡人等信息。

如图,分别输入 nameurlloginlangsalutation,可以对姓名、公钥网址、登录用户名、语言偏好、称呼进行配置。

在修改信息的过程中,可能会要求输入 Admin PIN,即上一步中修改的第3项,注意和用户 PIN 区分开。

完成后,再次输入 list,可以看到此次很多信息都被更新了:

用户交互配置(可选)

输入 uif 1 onuif 3 on,为“签名”和“认证”操作启用“触摸确认”的功能,防止后台应用偷偷调用。

密钥迁移

注意事项

此步骤会将子密钥移动到 Canokey,移动之后子密钥会在电脑的 GPG 里永久消失

因此,如果你想让子密钥导入到多个 key,或者不想让其永久消失,就一定要将其导出到电脑里进行备份

一些建议

通常,我们会使用子密钥进行加解密 / 签名操作,而不是主密钥。这是因为主密钥可以随时吊销子密钥,并生成新的子密钥。仅使用子密钥,可以最大程度确保安全。

对于主密钥,我们应该将其保存至一个冷备份容器中(比如开了 Bitlocker 的磁盘/虚拟磁盘)。

有关密钥生成,导入/导出的各种操作可以参考我之前的这篇文章

开始操作

开始前,请确认你已经完成了密钥的备份操作(注意事项中的内容)。

下面正式开始迁移操作,首先进入密钥编辑模式:

1
gpg --edit-key 52838AC66F9C7F0E 【替换为你自己的】

进入编辑模式的情景如下图,这里稍微有点不同,我的界面上有 card-no: F1D0 013135E1,因为之前我将它导入过另一个 canokey,且将私钥从电脑上删除了

因此,需要有一些额外的步骤(如果无此情况请跳过),否则 GPG 会一直认为私钥在先前的卡中,导致

  • 无法使用 keytocard 转移到新的卡(提示 KEYTOCARD failed: Unusable secret key),因为密钥环中实际上不存在私钥。
  • 也无法将私钥删除(--delete-secret-keys 提示 delete key failed: Not found)。
  • 无法从磁盘上重新导入私钥(提示 unchanged/not changed),使用 --edit-key 查看发现仍然绑定原先的卡(无法解除绑定)。
  • 使用--delete-keys 删除后重新导入,也并不能解绑。

要解决问题,核心思路为:

  1. 告诉 GPG Agent :“这个私钥和原先的卡不绑定”。
  2. 重新把私钥从硬盘导入进密钥环(独立存在)。
  3. 使用编辑功能,把私钥转移到新的硬件中去。

实际上,笔者认为 GPG 应该提供切换智能卡的命令选项,尤其是对于硬件密钥意外损坏,需要更换的情况,能省事不少。但就目前看来,这似乎还没有实现。

总之,经过多方查找,笔者终于得到了解决方案,有两种方法:

该方法仅需要:你在磁盘上拥有子密钥的私钥。

首先执行:

1
gpg --with-keygrip -k 52838AC66F9C7F0E

得到下图:

随后,根据这些 keygrip,找到并删除这个路径下全部对应的文件,以清除缓存的绑定关系:

1
~/.gnupg/private-keys-v1.d/[KEYGRIP].key

删除完文件后,需要重启一下 agent:

1
gpgconf --kill gpg-agent

随后,再从磁盘上重新导入子密钥的私钥:

1
gpg --allow-secret-key-import --import ~/xxx_sec.key

此时的私钥就变得普普通通,不再和硬件相关联。

该方法成功的前提:你在磁盘上拥有完整的私钥链

完整私钥链是指,你曾经对主密钥进行过导出,且导出时没有在密钥 ID 后面加上 !

例如,使用如下命令所导出的私钥,在接下来的操作步骤中是可用的。

1
gpg -a -o /path/to/gpg_all_sec.key --export-secret-keys 52838AC66F9C7F0E

操作步骤如下:

  1. 进入到 key 编辑模式。

  2. 分别执行 key 1key 2key 3,选中三个子密钥。

  3. 执行 delkey 将它们删除。

  4. 输入 save 保存。

  5. 重新导入私钥,使用命令:

    1
    gpg --allow-secret-key-import --import /path/to/gpg_all_sec.key
  6. 此时,所有私钥被重新导入,且清除了绑定关系。

===== 手动分割 =====

处理完额外的步骤,接下来就比较简单了,主要就是“选定密钥,并将其迁移”,可以在编辑模式下使用 key N 这样的命令对密钥选中。

如前文所说,我们只需将子密钥导入到 Canokey 即可。因此,我们首先选中第一个子密钥,即输入 key 1(主密钥编号是 0)。这里我们看到,框中的部分多了一个小星号,表示被选中:

选中完成后,输入 keytocard,即可永久将私钥移动到 Canokey 中,再次提醒,如有需要请做好备份!

执行时,会让你选取将密钥存储在哪个槽位,我们按 GPG 密钥的实际用途选就可以了(加密、签名和认证),如下图:

接下来,重复执行上面的步骤:

  • 首先再次输入 key 1,取消选中。
  • 输入 key 2 选中下一个私钥。(每次只选中一个)
  • 再次输入 keytocard,进行迁移。

最后,在迁移完所有私钥后,可以输入 save 保存对密钥的修改。(Tips:如果你要一次性导入密钥到多个 Canokey,此时可以不 save,直接退出,这样私钥不会被删除。导完最后一个 canokey 以后再执行保存,以建立私钥到智能卡的映射)

后续检查

完成密钥保存后,使用 gpg --card-status 读取 Canokey 确认密钥情况如下图:

可见,私钥已经正确导入到了智能卡中,接下来可以把主私钥删除(做好冷备份的前提下),提高安全性:

1
gpg --delete-secret-keys 52838AC66F9C7F0E

删除后,再次运行命令,确认密钥状态:

1
2
gpg --list-key 52838AC66F9C7F0E
gpg --list-secret-key 52838AC66F9C7F0E

如图,主密钥从sec变为pub,代表仅存储了主密钥的公钥;而子密钥从ssb变成了sub,也代表了子密钥只有公钥。

查看私钥,发现其带有 # 标记,表示私钥迁移到了硬件中:

至此,密钥迁移完成,可以愉快使用了。

参考

以下文章可能也会对你有所帮助:

  1. 生成GPG密钥并导入到Yubikey
  2. GPG 物理密钥从安装到日常使用
  3. gpg2: How to get rid of "Please insert card with serial number", getting the same key from a different card / Yubikey