跳到主要内容

使用GPG管理Yubikey密钥

前不久入手了一块 Yubikey,主要是用来为日常的账号做二次验证用。不过 Yubikey 除了用作二次验证的物理设备之外还可以充当智能卡,往里面放密钥。现在 Yubikey 不仅支持 RSA,还支持 ECC ,感觉拿来做日常加密签名还是不错的。 不过本人环境现在主要是 Windows,按官方文档在 Windows 下安装的 Gpg4win 在生成 ECC 密钥的时候会出现以下警告:

warning: lower 3 bits of the secret key are not cleared

这是ChatGPT的解释

这条警告指出在处理密钥时,密钥的最低 3 位没有被清除。在计算机安全中,"清除密钥的位" 意味着在密钥不再需要时,确保内存中的密钥值被重置或擦除,以防止密钥泄露。
可能的原因:这可能是 GPG 在 Windows 上的一个特定行为或一个未解决的问题。密钥生成过程中的这种微小差异可能是由于不同操作系统的内存管理方式或 GPG 在不同平台上的实现细节差异造成的。
安全影响:这通常是一个相对较小的安全问题,但它表明密钥生成过程中可能存在细微的安全疏漏。然而,除非存在高级别的安全威胁,否则这种情况不太可能被利用。

但是在Linux下没有这个警告,我猜是版本差异导致的。不过既然Linux下没有警告我就在Linux下生成了。

最后密钥是在 WSL2(Debian) 下生成,然后将密钥导入 Yubikey 是在 Windows 下完成的。

安装GnuPG

https://gnupg.org/download/index.html Windows 和 WSL2 下都要装。Windows 就装 Gpg4win,WSL2 下就用 apt 安装。

apt install gpg

生成密钥

Windows 的硬盘会被挂载到 WSL2 下的 /mnt 目录下,为了在 Windows 下可以获取成生的密钥,这里以 D 盘为例,在 /mnt/d 目录下生成密钥。

mkdir -p /mnt/d/gpg
cd /mnt/d/gpg
gpg --full-gen-key

按照提示生成密钥即可,我在 2.2.27 版本下没有看到有 ECC 密钥生成的选项,需要进到专家模式才可以,也许新版会默认有。 专家模式用 --expert 选项开启。

gpg --expert --full-gen-key

查看生成的密钥

公钥用小写的 -k 选项查看。

gpg -k

私钥用大写的 -K 选项查看。

gpg -K

分别生成加密、签名、验证密钥

gpg --edit-key [密钥 ID]

然后会进入到 gpg 的交互模式,输入addkey

gpg> addkey

加密和签名的密钥都没有难度,按提示操作即可。 验证密钥需要在专家模式下操作:

gpg --expert --edit-key [密钥 ID]

在这里选择 11,自定义密钥用途。

gpg> addkey
Please select what kind of key you want:
(3) DSA (sign only)
(4) RSA (sign only)
(5) Elgamal (encrypt only)
(6) RSA (encrypt only)
(7) DSA (set your own capabilities)
(8) RSA (set your own capabilities)
(10) ECC (sign only)
(11) ECC (set your own capabilities)
(12) ECC (encrypt only)
(13) Existing key
(14) Existing key from card
Your selection? 11

默认会带签名功能,先输入 A 把验证功能开启:

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Sign

(S) Toggle the sign capability
(A) Toggle the authenticate capability
(Q) Finished

Your selection? A

然后输入 S 把签名功能关闭:

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Sign Authenticate

(S) Toggle the sign capability
(A) Toggle the authenticate capability
(Q) Finished

Your selection? S

最后是 Q 完成:

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Authenticate

(S) Toggle the sign capability
(A) Toggle the authenticate capability
(Q) Finished

Your selection? Q

最后查看一下密钥,看看是不是三类密钥都有了:

# gpg -K --keyid-format LONG
----------------------------------------------
sec ed25519/XXXXXXXXXXXXXXXX 2022-04-21 [SC]
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
uid [ultimate] xxx <xxx@xxx.com>
ssb cv25519/XXXXXXXXXXXXXXXX 2022-04-21 [E]
ssb ed25519/XXXXXXXXXXXXXXXX 2022-04-21 [S]
ssb ed25519/XXXXXXXXXXXXXXXX 2022-04-21 [A]
----------------------------------------------

生成吊销证书

万一密钥泄漏可以通过吊销证书使密钥无效化。生成吊销证书以备不时之需。

gpg --output revoke.asc --gen-revoke [密钥 ID]

导出和导入密钥

密钥是在 WSL2 下生成的,需要在 Windows 上导入一遍。顺便导出密钥可以做个备份。 导出:

gpg --armor --output public.asc --export [密钥 ID]
gpg --armor --output private.asc --export-secret-keys [密钥 ID]

看到一些教程还会用 --export-secret-sub-keys 单独导出子私钥,查了一遍官方文档,官方文档不推荐这样做,除非你是真的只想单独导出子私钥。 比如你害怕主私钥泄漏,不想在新电脑上导入主私钥,只想导入子私钥,就可以用 --export-secret-sub-keys 导出子私钥,然后在新电脑上导入子私钥。 但是这里是要做完整的备份,所以不用 --export-secret-sub-keys 来单独导出子私钥。

导入(在 Windows 上):

gpg --import public.asc
gpg --import private.asc

还看到一些文章说重新导入用 --allow-secret-key-import 选项导入私钥,其实不用,--import 就行。 --allow-secret-key-import 已经没有用了,这里摘抄一下官方文档的说明:

--allow-secret-key-import
This is an obsolete option and is not used anywhere.

将密钥写入 Yubikey

Yubikey 官方文档有非常详细的说明,不在这里赘述。Yubikey 万一更新换代可能方法会变得不一样,还是时常查看官方文档的好。 https://developers.yubico.com/PGP/

在这里说一下几点实际操作和官方文档不一样的地方:

  • 官方文档在--edit-key进入编辑模式之后使用了toggle命令来在公钥和私钥之间切换。但是从 GnuPG 2.1 之后因为私钥存储方式的改变使得toggle命令变得不再有意义。所以如果用的是 2.1 版本之后的 gpg,toggle已经不是必须的了。
    参考:

    可以用help命令查看所有可用的命令。我使用的是 2.4.3 版本,确实没有toggle命令。

    gpg> help
    quit 退出此菜单
    save 保存并退出
    help 显示此帮助
    fpr 显示密钥指纹
    grip 显示 keygrip
    list 列出密钥和用户标识
    uid 选择用户标识 N
    key 选择子密钥 N
    check 检查签名
    sign 为所选用户标识添加签名 [* 参见下面的相关命令]
    lsign 为所选用户标识添加本地签名
    tsign 为所选用户标识添加信任签名
    nrsign 为所选用户标识添加不可吊销签名
    adduid 增加一个用户标识
    addphoto 增加一个照片标识
    deluid 删除选定的用户标识
    addkey 增加一个子密钥
    addcardkey 增加一个密钥到智能卡
    keytocard 移动一个密钥到智能卡
    keytotpm 使用本地 TPM 将密钥转换为 TPM 形式
    bkuptocard 移动一个备份密钥到智能卡上
    delkey 删除选定的子密钥
    addrevoker 增加一个吊销用密钥
    addadsk add an additional decryption subkey
    delsig 从所选用户标识上删除签名
    expire 变更密钥或所选子密钥的使用期限
    primary 标记所选的用户标识为主要
    pref 列出偏好设置(专家模式)
    showpref 列出偏好设置(详细模式)
    setpref 为所选用户标识设定偏好设置列表
    keyserver 为所选用户标识设定首选公钥服务器 URL
    notation 为所选用户标识的设定注记
    passwd 变更密码
    trust 变更信任度
    revsig 吊销所选用户标识上的签名
    revuid 吊销选定的用户标识
    revkey 吊销密钥或选定的子密钥
    enable 启用密钥
    disable 禁用密钥
    showphoto 显示选定的照片标识
    clean 压缩不可用的用户标识并从密钥上移除不可用的签名
    minimize 压缩不可用的用户标识并从密钥上移除所有签名
  • 写入完成之后可以在本地删除私钥了(公钥不要删)。

gpg --delete-secret-key [密钥 ID]

之后再查看私钥的时候会发现变成下面这个样子:

# gpg -K --keyid-format LONG
----------------------------------------------
sec# ed25519/XXXXXXXXXXXXXXXX 2022-04-21 [SC]
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
uid [ultimate] xxx <xxx@xxx.com>
ssb> cv25519/XXXXXXXXXXXXXXXX 2022-04-21 [E]
ssb> ed25519/XXXXXXXXXXXXXXXX 2022-04-21 [S]
ssb> ed25519/XXXXXXXXXXXXXXXX 2022-04-21 [A]
----------------------------------------------

对比一下上面前几节的输出,你会发现 sec 后面多了个 #,ssb 后面多了个 >。 # 的意思就是密钥(这里是主私钥)不在这台电脑上,> 的意思是密钥(这里是子私钥)指向了智能卡。

在移动到卡之后就不能再拔卡使用了,除非你在本机上彻底删除这种指向关系,可以在C:\Users[用户名]\AppData\Roaming\gnupg\private-keys-v1.d目录下把所有文件都删除,拔卡,再重新导入一遍公私钥即可(参考)。