站点图标 问谛居

Yubikey OpenPGP Card 密钥生成与导入

前记

因为主文章写到后面实在是太长了,所以进行了分篇,慢慢补充与完善,不然每次我 md 导入要改一堆东西,头大(

https://www.wd-ljt.com/post/1024/897.html
主文章

更新记录也会在主文章书写

PGP 密钥规划

但是我们可以直接使用 Yubikey 中 OpenPGP Card,这样就可以更方便的操作了,也不用浪费 PIV 的槽位,使用之前肯定是要生成与准备一部分子密钥的,总不能全部用主密钥吧,风险会不可控一点,作为实践标准我们需要使用主密钥授权子密钥,然后进行操作的时候使用子密钥。

演示时我们会使用 Yubikey 中 OpenPGP Card 功能如果要用 PIV 功能的话,以下有槽位分配推荐~

9a 槽位对应的是 PGP 的 Authentic(认证)

9b 槽位对应的是 PGP 的 Certification(证书管理),但是这里的 Certification 和你自己密钥的 Certification 无关,这个相当于是部署证书所需要验证的密钥,而不是你 GPG 生成的 Certification 的密钥(说白了密钥里面只能用到密钥里面,PGP 生成只是用于生成新的证书)

9c 槽位对应的就是 Signing(签名)

9d 槽位对应的就是 Encryption(加密)

PGP 主密钥生成

因为我是 Windows 所以就尝试性用了下其他博客写的 Gpg4win ,下载之后打开 GPA 他要求你生成一个私钥。

然后填写你的 Name (足够有辨识度),以及邮箱,然后他会默认给你生成一个 RSA2048 的主密钥,如果用 GNU 的最新版理论上是支持 RSA4096 不过 2048 就目前而言已经够用了,不知道为什么这个 GUI 页面是没有选择的余地,这时候我们尝试命令行。

这时候我们去 GnuPG/bin 里面找命令行工具

.\gpg.exe -help

查看指令帮助

输入

.\gpg.exe --full-generate-key

可以更客制化的设置你的密钥,然后他会让你选择加密方式,

gpg (GnuPG) 2.3.8; Copyright (C) 2021 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.

Please select what kind of key you want:
  (1) RSA and RSA
  (2) DSA and Elgamal
  (3) DSA (sign only)
  (4) RSA (sign only)
  (9) ECC (sign and encrypt) *default*
(10) ECC (sign only)
(14) Existing key from card
Your selection? 1

我们选择 1 ,毕竟作为主公钥得要完整的功能 DSA 感觉兼容性稍差(确实不懂,求饶过),所以我还是选择了 RSA。

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096

让你选择是多长的密钥,一般是 2048 3072 4096 里面选,因为强度是基础嘛,然后 2048 的兼容性理论是最好的,不过呢 Yubikey 5C 是支持 RSA 4096,所以我就直接选择 4096 了。

Requested keysize is 4096 bits
Please specify how long the key should be valid.
        0 = key does not expire
    <n> = key expires in n days
    <n>w = key expires in n weeks
    <n>m = key expires in n months
    <n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

这里让你选时间例如 1y 就是 1 年 1m 就是 1 月等等,当然因为是主密钥,我肯定是选择不会过期啦,如果你选择无限时间的话,就会被要求确认是否是无限时间。


GnuPG needs to construct a user ID to identify your key.

Real name: WDLJT
Email address: i@wd-ljt.com
Comment: Hello New World!
You selected this USER-ID:
  "WDLJT (Hello New World!) <i@wd-ljt.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: revocation certificate stored as '你的路径'
public and secret key created and signed.

pub   rsa4096 2022-10-22 [SC]
    你的公钥
uid                     WDLJT (Hello New World!) <i@wd-ljt.com>
sub   rsa4096 2022-10-22 [E]

最后你输入你的 Name,邮箱和备注,会弹出一个密码设置窗,必须使用正确的密码才能使用私钥,这样就可以新建一个主密钥啦~

PGP 子密钥生成

.\gpg.exe --expert --edit-key 你的密钥ID(pub)

就可以进入一个 key 的管理页面,--expert 的参数是进入专家模式(应该,但是我没找到文档)会有一些高级参数(?),当然不加也是完全可以的。

gpg (GnuPG) 2.3.8; Copyright (C) 2021 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.

Secret key is available.

sec rsa4096/*
    created: 2022-10-22 expires: never       usage: SC
    trust: ultimate     validity: ultimate
ssb rsa4096/*
    created: 2022-10-22 expires: never       usage: E
[ultimate] (1). WDLJT (Hello New World!) <i@wd-ljt.com>

gpg> addkey

当然这里可以看到你这个主密钥下面有两个不同的 key 一个是负责 SC 即为 Signing、Certification,另外是负责 E 即为 Encryption。我们直接输入 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?

我们前面有说过我们有 3 种不同的密钥,对于我的实验来说是需要 Encrypt 和 Sign,所以我们要进行两次 addkey,我先创建 Sign 的 key。

Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want?

还是一样的,我的 Yubikey 5C 是支持 4096 位的,所以我会选择 4096,当然为了兼容性可以选择 2048.

Requested keysize is 4096 bits
Please specify how long the key should be valid.
        0 = key does not expire
    <n> = key expires in n days
    <n>w = key expires in n weeks
    <n>m = key expires in n months
    <n>y = key expires in n years
Key is valid for? (0)

默认为 0,但是呢一般为了安全起见会使用定时 key,定时更换,又因为我比较懒不想太短所以我会选择 2y(即2年)

Key is valid for? (0) 2y
Key expires at 2024/10/21 21:20:03
Is this correct? (y/N) y
Really create? (y/N) y

会让你二次确认有效期,然后会确认是否创建,然后会弹出来你输入密码就是你当初创建的时候设置的。

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

sec rsa4096/*
created: 2022-10-22 expires: never usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/*
created: 2022-10-22 expires: never usage: E
ssb rsa4096/*
created: 2022-10-22 expires: 2024-10-21 usage: S
[ultimate] (1). WDLJT (Hello New World!) <i@wd-ljt.com>

你的专用于 Sign 的子密钥就有了~

同理我们再生成一个 Encrypt 的子密钥。

ssb  rsa4096/*
created: 2022-10-22 expires: 2024-10-21 usage: E

这样就好啦~ 不过呢为了之后用于认证 SSH 所以还是生成一个用于 Authentic 的子密钥

Possible actions for this RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Encrypt

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

Your selection? A

Possible actions for this RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Encrypt Authenticate

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

Your selection? E

Possible actions for this RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Authenticate

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

Your selection? S

Possible actions for this RSA key: Sign Encrypt Authenticate
Current allowed actions: Authenticate

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

Your selection?

选择 8 可以自定义你的证书,你可以输入对应的字符切换是否有没有某种功能一开始是 S E,这个时候我们就要输入 A E S 让他反选,输入 Q 确认。这个时候后和之前一样选择时间和强度后等待即可

ssb  rsa4096/*
    created: 2022-10-22 expires: 2024-10-21 usage: A

PGP 公私钥备份与 Revoke Key 备份

然后我们这时候需要把 Key 备份一下下,然后找个安全的地方存起来(当然我选择的办法是打印,比较原始)

gpg --armor --output secret-key.txt --export-secret-keys

--armor 用于以 Hex 输出 --output <path> 就是需要备份到的目录,--export-secret-keys 就是字面意思,如果需要分密钥导出的话,之前翻到了一篇博客,稍微引用下他的话

这会生成 secret-key.txt 文件,包含你的主密钥和所有子密钥,你也可以使用 export-secret-subkeys 指令来只导出单个/几个/所有子密钥,参数为至少一个密钥 id,以空格分开参数,如果 id 后跟感叹号,则表示单个导出,如果不跟则这个子密钥的所属的主密钥的下属所有子密钥都会导出(很拗口,就是所有同一主密钥的子密钥都会导出)。
来源:https://ghost.jonirrings.com/gpg-ssh-with-yubikey/

然后再导一个公钥出来

gpg --armor --output public.asc  --export 你的公钥ID

然后把每个 key 的 revoke 信息导出备份(其实只用主密钥就行,毕竟你其他密钥泄露了用主密钥直接签署 revoke 就行。),以防不时之需~,如果想要了解 revoke key 是用于什么的可以看看这一篇 Stack Overflow,revoke key 操作可以参考这篇博客

gpg --armor --output revoke.txt --gen-revoke 你的公钥ID

然后会让你确认是否要生成一个用于销毁你选择的密钥的证书。输入 y 确认。

sec  rsa4096/* 2022-10-22 WDLJT (Hello New World!) <i@wd-ljt.com>

Create a revocation certificate for this key? (y/N) y

然后会让你选择销毁这个 Key 的原因。

Please select the reason for the revocation:
0 = No reason specified
1 = Key has been compromised
2 = Key is superseded
3 = Key is no longer used
Q = Cancel
(Probably you want to select 1 here)
Your decision? 1

选择 0 是没有具体的原因 选择 1 是你的 Key 泄露了 选择 2 是你的 Key 已经更替了 选择 3 是你的 Key 不再使用 选择 Q 就是退出了 这里我们选择 1 因为是用于备用的,这个原因的区别只是会影响 Key 的吊销备注所以没有什么特别大的影响。

Enter an optional description; end it with an empty line:
> My Key is lost.

这里可以写具体原因,我就随便写了一句,再次换行即可保存。

Is this okay? (y/N) y
Revocation certificate created.

Please move it to a medium which you can hide away; if Mallory gets
access to this certificate he can use it to make your key unusable.
It is smart to print this certificate and store it away, just in case
your media become unreadable. But have some caution: The print system of
your machine might store the data and make it available to others!

最后让你确认下,然后会给你说注意事项。

将你的 PGP 公钥发送到公共服务器上

gpg --keyserver <Keyserver> --send-key <你的公钥 ID>

Keyserver 国内能访问到的有这几个,闲的没事的话都可以传一份(

甚至你还可以丢一份去自己的 Github Gist 让更多人知道你的公钥长啥样子(

keyserver.ubuntu.com
pgp.mit.edu

但请注意保护自己的隐私,毕竟你也不想在赛博世界上莫名其妙被追到家门口,请注意你的 Name 不要透露过多的隐私。

到此前期准备任务就结束了

重置默认密码

刚刚我们介绍了如何重置 PIV 的密码,这个时候我们要把智能卡的密码给重置了。

以下为默认信息

User Password:123456

Admin Password:12345678

先进入卡编辑模式

.\gpg.exe --card-edit

然后进入管理模式

gpg/card> admin
Admin commands are allowed

然后更改密码

gpg/card> passwd
gpg: OpenPGP card no. D*
detected

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? 3

输入 3 修改管理密码 输入 1 修改普通密码

请务必注意你有且仅有 3 次机会输入错误的 PIN,所以请务必看清楚他要的是什么 PIN,比如说这里的 Admin PIN,就是要输入 Admin PIN(默认值:12345678)

只写一个 PIN 的就是要求普通用户密码,修改完以后我们进入下一步

把 PGP 密钥丢进 Yubikey 里面

GnuPG Card Edit Tool 命令

gpg/card> help
quit quit this menu # 退出目录
admin show admin commands # 切换管理员模式,切换后会回显当前状态
help show this help # 显示本帮助
list list all available data # 列出所有可用数据
fetch fetch the key specified in the card URL # 从 Card 中设置的 URL 上获取公钥信息
passwd menu to change or unblock the PIN # 如果在管理员模式则会进入菜单,如果在普通模式会修改普通用户的 PIN 码
verify verify the PIN and list all data # 确认密钥是否正确并列出可用信息
unblock unblock the PIN using a Reset Code # 使用 Reset Code 解冻所有 Key (因为错误次数过多被冻结的 Key)

生成完子密钥之后,我们就该把这些密钥丢进去了,插入您的 Yubikey 然后输入

gpg --card-edit

可以查看您的 Key 的基本信息,也可以看到 PIN 与 Admin PIN 的剩余尝试次数

.\gpg.exe --card-edit
设备名:
Reader ...........: Yubico YubiKey OTP FIDO CCID 0
应用 ID(后续要用到)
Application ID ...: D*
应用协议类型:
Application type .: OpenPGP
版本:
Version ..........: 3.4
制造商:
Manufacturer .....: Yubico
序列号:
Serial number ....: *
持卡人名字:
Name of cardholder: [not set]
语言偏好:
Language prefs ...: [not set]
留言:
Salutation .......:
公钥 URL 链接:
URL of public key : [not set]
登录数据:
Login data .......: [not set]
签名 PIN 要求:
Signature PIN ....: not forced
密钥类型:
Key attributes ...: rsa2048 rsa2048 rsa2048
最长 PIN 长度:
Max. PIN lengths .: 127 127 127
PIN 码与 Admin PIN 码剩余尝试次数:
PIN retry counter : 3 0 3
签名计数器:
Signature counter : 0
KDF 设置:
KDF setting ......: off
UIF 设置:
UIF setting ......: Sign=off Decrypt=off Auth=off
Key 信息(之后塞进去再写)
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

然后在写入之前,可以考虑备份一次子密钥,因为你写入 Key 之后,您的子密钥会在本地移除,所以留个备份会比较好。

gpg --armor --output secret-key.txt --export-secret-subkeys

导出除了主密钥以外所有的 Key。保存方式自己定一般用外部存储,我们先走后面的步骤。

先进入 Key 编辑模式

.\gpg.exe --edit-key 您的公钥 ID
Secret key is available.
这里是主密钥,为 0 号 Key。
sec rsa4096/*
    created: 2022-10-22 expires: never       usage: SC
    trust: ultimate     validity: ultimate
这里是 1 号 Key。
ssb rsa4096/*
    created: 2022-10-22 expires: never       usage: E
这里是 2 号 Key。
ssb rsa4096/*
    created: 2022-10-22 expires: 2024-10-21 usage: S
这里是 3 号 Key。
ssb rsa4096/*
    created: 2022-10-22 expires: 2024-10-21 usage: E
这里是 4 号 Key。
ssb rsa4096/*
    created: 2022-10-22 expires: 2024-10-21 usage: A
[ultimate] (1). WDLJT (Hello New World!) <i@wd-ljt.com>

从上到下 ,从 0 开始,然后用 key <ID> 来切换 Key,例如我先把我的 Encrypt Key 写进去,选择 Key 3,前面会有一个 星号* 表示选中,然后输入 keytocard,选择正确的用途或槽位

gpg> keytocard
Please select where to store the key:
  (2) Encryption key
Your selection? 2

先输入 密钥密码 后输入 Card Admin PIN,请务必看清楚他要的是什么 PIN 或者密码。

如果不用 Ctrl + C 中断后再进来,需要反选刚刚选中的 Key,再选中你下一个要写入 Yubikey 的 Key。

写完之后再重新进入卡编辑模式查看写入情况

.\gpg.exe --card-edit

Reader ...........: Yubico YubiKey OTP FIDO CCID 0
Application ID ...: 您的卡 ID
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: 您的序列号
Name of cardholder: [not set]
Language prefs ...: [not set]
Salutation .......:
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa4096 rsa4096 rsa4096
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
KDF setting ......: off
UIF setting ......: Sign=off Decrypt=off Auth=off
Signature key ....: 当前证书公钥
    created ....: 2022-10-22 13:13:33
Encryption key....: 当前证书公钥
    created ....: 2022-10-22 13:30:07
Authentication key: 当前证书公钥
    created ....: 2022-10-22 13:38:35
General key info..:
sub rsa4096/密钥 ID 2022-10-22 WDLJT (Hello New World!) <i@wd-ljt.com>
sec   rsa4096/密钥 ID created: 2022-10-22 expires: never
ssb   rsa4096/密钥 ID created: 2022-10-22 expires: never
ssb   rsa4096/密钥 ID created: 2022-10-22 expires: 2024-10-21
ssb   rsa4096/密钥 ID created: 2022-10-22 expires: 2024-10-21
ssb   rsa4096/密钥 ID created: 2022-10-22 expires: 2024-10-21

设置使用 Yubikey OpenPGP 时需触摸确认

cd [您的 Yubikey Manager 路径]
您的 Yubikey Manager 路径> ykman openpgp keys set-touch sig on
Enter Admin PIN:
Set touch policy of signature key to on? [y/N]: y #签名使用
您的 Yubikey Manager 路径> ykman openpgp keys set-touch aut on
Enter Admin PIN:
Set touch policy of authentication key to on? [y/N]: y #验证使用
您的 Yubikey Manager 路径> ykman openpgp keys set-touch enc on
Enter Admin PIN:
Set touch policy of encryption key to on? [y/N]: y #加密使用

Yubikey Manager CLI 语法 Document

退出移动版