讲道理,我觉得再一再二不能再三,踩坑踩的太多了就不能再踩了,不然智商都没了……

背景

说到踩坑,其实我在 Yubikey 里踩的坑那可谓数不胜数:

第一次买了Key,找了个很渣的教程,上去直接 newcardkey 就生成了一个,存卡里不说,还没法备份……导致直接用这个key用了很久,然后很悲剧的被同学“物理损坏”了……

后面不甘心,又买了个,这回我可是百般呵护啊:单独生成 Key ,准备了加密备份,断网操作,丢到可移动存储设备里,写卡,发布正可谓一气呵成……

然后还是难逃命运的巨轮:备份物理损坏,过了一段时间后,这个 key 莫名其妙的无法签名 & 加密数据。哇~ 从小到大哪里受过这个气,排除很多问题后依旧无法解决……

然后就产生了下面的这个对话:

就这样,为了防止自己继续踩坑,我打算用此文祭奠我那可怜的卡……和 Key……们……

介绍

先上个 Wikipedia 的 link 吧。

GNU Privacy Guard (GnuPG or GPG) is a free software replacement for Symantec’s PGP cryptographic software suite.[5] GnuPG is compliant with RFC 4880, which is the IETF standards track specification of OpenPGP. Modern versions of PGP are interoperable with GnuPG and other OpenPGP-compliant systems.

GnuPG is part of the GNU Project, and has received major funding from the German government.[6]

关于这个,千万别和 PGP 搞混了:

Pretty Good Privacy (PGP) is an encryption program that provides cryptographic privacy and authentication for data communication. PGP is used for signing, encrypting, and decrypting texts, e-mails, files, directories, and whole disk partitions and to increase the security of e-mail communications. Phil Zimmermann developed PGP in 1991.[2]

PGP and similar software follow the OpenPGP standard (RFC 4880) for encrypting and decrypting data.

通宵读了很多文献啊,感觉这次准备应该是万无一失了。

上刀

本文的目的有两个,一个是防止自己再踩坑,另一个是写出来给大家看的(弱

显然,看标题都知道要把 Key 丢到 Yubikey 里(或者别的什么 SmartCard 里)。

材料准备

你需要准备以下材料才能继续调配黑魔法药水……

  • 能上网的电脑
  • Linux / MacOS 操作系统(别问我为啥不用 Windows)
  • 😩 <- 一张苦逼的脸
  • 能拔掉的网线
  • 一个物理存储器,方便存备份密钥
  • 一张由人类生产的靠谱的智能卡(SmartCard)
  • 一颗能折腾的心💖

调配药水前的准备工作

首先联网,装好 GnuPG 软件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# CentOS
sudo yum -y install gpg
# Debian / Ubuntu
sudo apt install gpg
# Arch Linux
sudo pacman -S gpg
# MacOS
brew install gpg
# Fedora
sudo dnf install gnupg

不过关于 Mac,还有个很方便的 Suite 可以用:GPGTools

如果装了 GPGTools 就不用再用 brew 装了。

生成 KeyPair

嗯,这才是正片环节。
接下来的步骤请断网操作。

Step I. 生成密钥对
1
gpg --gen-key
1
2
3
4
5
6
7
8
9
10
gpg (GnuPG/MacGPG2) 2.2.0; Copyright (C) 2017 Free Software Foundation, Inc.
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 (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
Your selection?

当然果断选1啦~

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

这里说下,Yubikey4 最高支持 4096 位的Key,所以这里生成当然可以直接用 4096 位。

1
2
3
4
5
6
7
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)

有效期嘛,看你心情了,反正又不是不能改。

1
2
3
4
5
6
7
GnuPG needs to construct a user ID to identify your key.
Real name: FirstName LastName
Email address: example@example.com
Comment: Comment Here
You selected this USER-ID:
"FirstName LastName (Comment Here) <example@example.com>"

格式列在上面了,生成出来的 Key 会遵照这个格式显示的

这里呢,输入一个足够强的私钥保护密码就可以了。记住了,不能丢。

1
2
3
4
5
6
public and secret key created and signed.
(pub开头是主密钥 密钥格式+长度/KeyID 生成时间 密钥功能)
pub rsa4096/0x0000000000000000 2017-10-07 [SC]
FFFFFFFFFFFFFFFF0000000000000000(这个是Key指纹)
uid FirstName LastName (Comment Here) <example@example.com> (这是用户ID信息)
sub rsa4096/0x0000000000000000 2017-10-07 [E]

关于功能的解释如下:

  • 签名(S, Sign)
  • 证书(C, Certificate)
  • 加密(E, Encryption)
  • 认证(A, Authenticate)

之后你的密钥对就生成好了,存放在 Keyring 里。

Step II. 生成子密钥
1
2
3
gpg --expert --edit-key [KeyID]
gpg> addkey

过程和第一步差不多,一共生成三个子密钥,长度都是 4096 位就可以。

先是生成一个签名 RSA (sign only) 用的。

然后再运行一次,生成一个加密 RSA (encrypt only) 用的。

然后再运行一次,生成一个认证 RSA (set your own capabilities) 用的。

这一步有点不同,大致过程是这样的,照葫芦画瓢就行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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)
Your selection? 8
Possible actions for a 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? s
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Encrypt
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? e
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions:
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? a
Possible actions for a 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? q

剩下的步骤都一样。
之后呢,保存退出。

1
gpg> save
Step III. 吊销凭证

建议生成一个对应的吊销凭证,一般来说,新版的 GPG 会自动生成好,存放到 ~/.gnupg/openpgp-revocs.d 目录里, 并且会在刚才生成时候告诉你:

1
asgpg: revocation certificate stored as '~/.gnupg/openpgp-revocs.d/[FINGERPRINT].rev'

当然你也可以手动生成一个

1
gpg --ken-revoke [KeyID]
Step IV. 导出密钥

这步很关键,拿出准备好的物理存储器,插上,然后执行导出命令

1
2
gpg --armor --output public.gpg.asc --export [KeyID]
gpg --armor --output private.gpg.asc --export-secret-keys [KeyID]

然后把这两个文件存到你的存储器里。

你也可以把第三步的吊销凭证也放进去,以防万一。

Step V. 上传

这里请拔出你的存储器,重新插上你的网线,然后继续。

上传就很简单了,一个命令的事情。

1
gpg --send-keys [KeyID]

当然你要想上传到其他的服务器,也是可以的:

1
gpg --keyserver hkp://subkeys.pgp.net --send-keys [KeyID]

我个人习惯用 hkp://pgp.mit.edu

Step VI. 配置 Yubikey

唉,总算到了本文一开始写的目的这里了。

插入你的 Yubikey,先做一下初始化:

1
gpg --card-edit

首次使用一定要先配置一下卡密码(PIN Code),一共有三组 PIN,分别为:

  • PIN (签名常用的 默认 123456)
  • Admin PIN (配置卡用的 默认 12345678)
  • Reset Code (重置Sign PIN用的)

这三个 PIN 可以是字母和数字。

  • PIN 码是平时最常用的密码,如果输错三次就会被锁定,需要使用 Reset Code 来解锁,Reset Code 输错三次,只能物理重置
  • Admin PIN 是管理卡信息(如添加密钥、修改密码)使用的密码,不能短于 8 位,输错三次则管理功能被锁定,只能物理重置记住,千万不要在需要输入 Admin PIN 的时候输入短于 8 位的密码,这样会直接锁定

修改方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
gpg/card> admin
Admin commands are allowed
gpg/card> passwd
gpg: OpenPGP card no. Dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx detected
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Your selection?

分别执行 1,3,4然后退出就可以。

你可以继续设置一些其他信息,比如持卡人姓名、公钥的URL等等,也可以下次再弄。修改这些信息,需要用到 Admin PIN。

Step VII. 存入密钥

先说一句:不推荐把主密钥放进 YubiKey 里,因为主密钥还有一个重要功能就是维持社交关系,时间流逝,子密钥可能经常变化,而别人只需要记住你的主密钥ID就可以随时更新。如果把主密钥放进 YubiKey 里,万一丢了,那么你就无法证明你是你了。

先编辑刚刚的 Key:

1
gpg --edit-key [KeyID]

然后使用 key [X] 来选中需要操作的子密钥。

Yubikey 有三个 Slot,分别可以存 S, A, E三种密钥。

选好 Key 后,使用 keytocard 命令存入卡的对应位置。

最后保存

1
gpg> save

从此,你的私钥 Key 再也无法备份保存了。

原理大致是这样的:GPG 将私钥存入卡后,向私钥添加了一个标志位,说明此私钥受到卡保护,需要插入卡才能解锁私钥使用。所以你即使现在导出私钥,也是导出了带有卡保护标志位的私钥,丢失卡后照样无法使用私钥。

这时候执行 gpg -K 可以发现你的 Key 多出一行内容:Card serial no. = xxxx xxxxxxxx,这个就是卡的序列号了,说明私钥受到卡保护。

Step VIII. 测试

你可以用这种方法测试一下卡签名功能:

1
echo foo | gpg --clearsign

正常来说应该会弹出输入PIN的地方,输入PIN后会完成签名,并把结果打印在屏幕上。

Step IX. 奇技淫巧
  • 如果你在第六步末尾设置了公钥URL,那么以后你就可以在其他电脑/设备上插入 Yubikey,在 gpg --card-edit 中执行 fetch 命令快速取回公钥并存入 Keyring 中。
  • 当然,如果你觉得签名/加密/认证操作不够酷炫,Yubico 有一个 ykman 工具可以让 Yubikey 仅在手指按后才会执行签名/加密/认证操作。
Step X. SSH 认证

第二步生成的子密钥里有一个认证(A)用的密钥还记得么?

这里可以用于 SSH 认证。

首先确认下有没有装 gpg-agent:

1
2
3
gpg-agent
gpg-agent[7002]: gpg-agent running and available

没有的话就装一个咯

1
brew install gpg-agent

将以下脚本加入到 shell 的 rc 里。我用的是 zsh,在 ~/.zshrc

1
2
3
4
5
6
if [ -f "${HOME}/.gpg-agent-info" ]; then
. "${HOME}/.gpg-agent-info"
export GPG_AGENT_INFO
export SSH_AUTH_SOCK
export SSH_AGENT_PID
fi

将以下内容添加到 ~/.gnupg/gpg-agent.conf (其他OS用户请参照文档自行替换PINEntry)

1
2
3
4
5
pinentry-program /usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac
default-cache-ttl 600
max-cache-ttl 7200
enable-ssh-support
write-env-file

然后执行一下一个我找了半天文档没找到还Bug巨多的命令:

1
/usr/local/MacGPG2/bin/gpgkey2ssh [KeyID]

会生成一个符合 OpenSSH 标准的 SSH Public Key 给你,把这个 Key 放服务器 authorized_keys 里就可以用 Yubikey 实现登录服务器了。

然而,我死活找不到这个工具了……

找了半天,发现官方发了个声明

gpgkey2ssh has gone, –export-ssh-key is here.

gpgkey2ssh was more or less a debugging tool but turned out to be useful
to many. However, it was pretty limited and did not support modern ssh
keys. An easier solution to export OpenPGP’s public keys in the SSH
public key format is by adding a command to gpg.

怪不得没文档还一堆Bug,原来就是个调试工具啊(

那行了,现在的新方法是使用 --export-ssh-key 参数来生成

1
gpg --export-ssh-key [KeyID]

将输出的内容贴到服务器的authorized_keys 里,愉快的玩耍吧(