可能是最好的 Yubikey + GPG/SSH 智能卡教程
首先,经过了六个小时的栽坑和爬坑,我可能得开一个地图炮:
目前中文互联网真的找不到好教程。
当然这也其实没什么,因为我等会要开一个更大的地图炮:
英文互联网也不咋地。
好了,绕了太多坑,就此打住。
GPG 那些事儿
想必地球人都知道,PGP (Pretty Good Privacy)可能是世界上最优秀的非对称加密工具,公私钥体系真正避免了对称加密会带了的密钥泄漏的问题。而且基于去中心的模型,绕过了 CA,让每个人都能接触到 Web of Trust (WOT)。而 GPG (GNU Privacy Guard)则是 PGP 的开源 GNU 实现,不要钱。
感谢 CCTV,感谢 RMS。最要感谢的,是那些精通给产品取名艺术的人,他们没有当产品经理可能是上天的旨意。
如果不清楚 PGP 是咋工作的,维基百科走起。当然不知道的也估计不会再看这篇文章,不谈了。
为什么我们需要三个新的子密钥
显然,这篇文章的目的是把 GPG 密钥扔到 Yubikey(或其他由人类生产的智能卡)里并且使其能够工作。从安全的角度来说,你需要三个新的子密钥,分别是:
- E (Encryption) 加密
- S (Signing) 签署
- A (Authentication) 认证
因为智能卡会丢,所以建议在电脑上生成,导出私钥离线备份好,然后扔到 Yubikey 里去。这不是多此一举,因为扔进去就出不来了。正确的 PGP 使用习惯很重要,用 Master Key 签署是一件很不好的事情。在可能的情况下,用子密钥干活,Master key 仅在子密钥过期的时候用来生成一个新的。除非特殊情况,否则不要生成有效期为 forever 的密钥。我就用的一年,正好,renew 六十次就差不多嗝屁了。
如果你不用 SSH,那你就不用生成那个认证密钥了,SE 够用。
GPG 发行版
建议所有的 Mac 用户都安装一个 GPGtools。brew 应该也行,不知道有些包会不会缺。
Linux 用户,你是最棒的。
生成子密钥
显然,我们该生成三对新的子密钥了。这里假设你之前已经生成了 GPG 主密钥。如果没有,自己生成一下也很简单,不谈了。
gpg --expert --edit-key [email protected]
生成签名子密钥:
gpg> addkey
Key is protected.
You need a passphrase to unlock the secret key for
user: "WhoAmI <[email protected]>"
4096-bit RSA key, ID 0x233333333333, created 2016-10-28
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? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want?(密钥长度) (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) (有效期)1y
Key expires at Sat 28 Oct 2017 12:00:00 PM UTC
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.
等一会就好了,接着照葫芦画瓢,生成加密用的:
gpg> addkey
Key is protected.
You need a passphrase to unlock the secret key for
user: "WhoAmI <[email protected]>"
4096-bit RSA key, ID 0x233333333333, created 2016-10-28
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? 6
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want?(密钥长度) (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) (有效期)1y
Key expires at Sat 28 Oct 2017 12:00:00 PM UTC
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.
认证(auth)密钥:
gpg> addkey
Key is protected.
You need a passphrase to unlock the secret key for
user: "WhoAmI <[email protected]>"
4096-bit RSA key, ID 0x233333333333, created 2016-10-28
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
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want?(密钥长度) (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) (有效期)1y
Key expires at Sat 28 Oct 2017 12:00:00 PM UTC
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.
很好,保存一下退出
gpg> save
建议导出一下公私钥,离线存储到一个安全的地方。
gpg --export -a "[email protected]" > public.key
gpg --export-secret-key -a "[email protected]" > private.key
备份!
备份!
如果有必要,可以将公钥上传到公钥服务器,命令是这个
gpg --send-keys
Yubikey 初始化
首先,插入 Yubikey,
gpg --card-edit
修改个密码吧,注意有两个,一个是 PIN
,一个是 Admin PIN
,默认密码分别是 123456
和 12345678
。Reset Code
也建议设一个,初始化的时候用。
gpg/card> passwd
gpg: OpenPGP card no. xxxxxxxxxxxxxxxxxxxx detected
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Your selection? 1
PIN changed.
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Your selection? 3
PIN changed.
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
其他的 URL,用户名字就不说了,喜欢的话就改吧。
如果有强迫症,可以用 newcardkey
直接在卡里生成新密钥。但是显然这会导致私钥完全无法导出,所以不详谈了。
将私钥装载到 Yubikey
gpg --edit-key "[email protected]"
会显示出你的主密钥和子密钥。
gpg> toggle
用 key【空格】数字
定位到子密钥,先装载 signing 子密钥。数字按实际的来,不一定是1
:
gpg> key 1
gpg> keytocard
Please select where to store the key:
(1) Signature key
(3) Authentication key
Your selection? 1
```
好了。先反选签名密钥,然后装载加密密钥:
```
gpg> key 1
gpg> key 2
gpg> keytocard
Please select where to store the key:
(2) Encryption key
Your selection? 2
```
同样,反选加密密钥,装载认证密钥至 Yubikey:
```
gpg> key 2
gpg> key 3
gpg> keytocard
Please select where to store the key:
(3) Authentication key
Your selection? 3
```
最后保存:
```
gpg> save
```
这样就可以了!
私钥想备份你现在也没办法了。
#### 删除,以及测试
可以删除本地 GPG 钥匙环里的公私钥,用 Yubikey 测试一下。如果可以的话,以上的就没问题了。
删除公钥:
```
gpg --delete-keys "[email protected]"
```
删除私钥:
```
gpg --delete-secret-keys "[email protected]"
```
这样电脑相当于不认识这个密钥了。插入 Yubikey,执行以下命令从公钥服务器获取公钥:
```
gpg --card-edit
gpg/card> fetch
gpg/card> quit
```
运行 `gpg --card-status`,系统会建立一个像是指针的东西,指向智能卡。
测试签署一下:
```
gpg -s testfile
```
没问题的话就可以了。如果还想帅一点,Yubico 有一个 `ykman` 工具可以让 Yubikey 仅在手指按后才会执行签名/加密/认证操作。至此,GPG 配置完毕。
### SSH!
如果有 VPS,那么用 Yubikey 连接是一件很装逼的事情,不可错过。
需要指出的是,绕了好多坑,而且教程特别少。会尽量详细一些。
安装 `gpg-agent`:
```
brew install gpg-agent
```
将以下脚本加入到 shell 的 rc 里。我用的是 zsh,在 `~/.zshrc`
```
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`
```
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 多,没有文档,要严格按照步骤来。
```
/usr/local/MacGPG2/bin/gpgkey2ssh DD091C21 > GPGSSH.pub
```
`DD091C21` 是我的认证子密钥,替换为自己的。直接运行会报错,必须按照这个格式。
这会输出一个符合 openssh 标准的公钥,将这个放在 VPS 的 `~/.ssh/authorized_keys` 里。至于如何配置 VPS 使用公钥认证,不再赘述。
注销重新登录用户,确保 `gpg-agent` 有运行。插入 Yubikey,
```
gpg --card-status
```
这步很重要,一定要执行。我在这里浪费了一个多小时。
```
ssh-add -L
```
看看有没有正常工作,如果有,会输出你的公钥。
如果以上都没有问题,那么恭喜,已经可以使用 Yubikey 来 ssh 了。像往常一样,`ssh usename@hostname` 就可以连接 VPS。如果用 `ykman` 配置了 `touch`,在灯光闪烁的时候摁一下即可,太炫酷了。
#### Clarification
`gpg-agent` 意义是什么?到底是用 `ssh` 来连接还是用 `gpg`?是不是还要一个 `openssh` 格式的私钥?这是少有教程提及的问题,也是我当时所疑惑的问题。
- 连接依然使用的是 `ssh`。和以往没有任何区别。
- 认证过程中,相当于 `openssh` 把公私钥钥匙环的部分“承包”给了 `gpg-agent`。`gpg-agent` 代替了 `ssh-agent` 的环境参数,接管了这部分内容。因此,密钥的解锁、认证,都是按照 `gpg` 的规则来。
- 不需要 `openssh` 格式的私钥,只需要导出一个公钥。使用方法和正常的公钥一样。私钥即是 `gpg` 的 "Authentication" 子密钥。
- 需要注意的是,`pgp` 的 Master key 是一个签名密钥,可以放在签名槽里。但这不是一个好的做法。
- 一定要备份主密钥__私钥__。这样一旦子密钥丢失,也还有机会吊销。
好累啊,坑太多。