YubiKey4 我踩过的那些坑
讲道理,我觉得再一再二不能再三,踩坑踩的太多了就不能再踩了,不然智商都没了……
背景
说到踩坑,其实我在 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)
- 😩 一张苦逼的脸
- 能拔掉的网线(你用WiFi就关闭WiFi好了)
- 一个物理存储器(U盘),方便存备份密钥
- 一张由人类生产的靠谱的智能卡(SmartCard)
- 一颗能折腾的心💖
调配药水前的准备工作
首先联网,装好 GnuPG 软件。
1 | # CentOS |
不过关于 Mac,还有个很方便的 Suite 可以用:GPGTools
如果装了 GPGTools 就不用再用 brew 装了。
生成 KeyPair
嗯,这才是正片环节。
接下来的步骤请断网操作。
Step I. 生成密钥对
1 | gpg --gen-key |
显示欢迎信息,并提示选择 key 类型
1 | gpg (GnuPG/MacGPG2) 2.2.0; Copyright (C) 2017 Free Software Foundation, Inc. |
当然果断选1啦~
1 | RSA keys may be between 1024 and 4096 bits long. |
这里说下,Yubikey4 最高支持 4096 位的Key,所以这里生成当然可以直接用 4096 位。
1 | Please specify how long the key should be valid. |
有效期嘛,看你心情了,反正又不是不能改。
1 | GnuPG needs to construct a user ID to identify your key. |
格式列在上面了,生成出来的 Key 会遵照这个格式显示的。
这里呢,输入一个足够强的私钥保护密码就可以了。记住了,不能丢。
1 | public and secret key created and signed. |
关于功能的解释如下:
- 签名(S, Sign)
- 证书(C, Certificate)
- 加密(E, Encryption)
- 认证(A, Authenticate)
之后你的密钥对就生成好了,存放在 Keyring 里。
Step II. 生成子密钥
1 | gpg --expert --edit-key [KeyID] |
过程和第一步差不多,一共生成三个子密钥,长度都是 4096 位就可以。
先是生成一个签名 RSA (sign only)
用的。
然后再运行一次,生成一个加密 RSA (encrypt only)
用的。
然后再运行一次,生成一个认证 RSA (set your own capabilities)
用的。
这一步有点不同,大致过程是这样的,照葫芦画瓢就行:
1 | Please select what kind of key you want: |
剩下的步骤都一样。
之后呢,保存退出。
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 | gpg --armor --output public.gpg.asc --export [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 | gpg/card> admin |
分别执行 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 | gpg-agent |
没有的话就装一个咯
1 | brew install gpg-agent |
将以下脚本加入到 shell 的 rc 里。我用的是 zsh,在 ~/.zshrc
1 | if [ -f "${HOME}/.gpg-agent-info" ]; then |
将以下内容添加到 ~/.gnupg/gpg-agent.conf
(其他OS用户请参照文档自行替换PINEntry)
1 | pinentry-program /usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac |
然后执行下面这个我找了半天文档没找到,还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
里,愉快的玩耍吧(
其他玩法
sudo Pam.d 认证
简单点说,就是使用 yubikey 去授权你的登录与 sudo 操作,我做了个 gif 可以看看效果:
实现的话其实也不是很难,先说 MacOS:
准备工作
- Time Machine Backup - 刚道理啊,这个很重要的,万一改错了还能回来呢……岂不美哉(
- Fido U2F Yubikey - 别买 NFC 的,iOS 还不支持,Yubikey 4 就可以用
Update: Yuubikey 5 支持 NFC 了,是可以用的 - Homebrew for OS X (or higher) - 需要装个
pam-u2f
。没装过 Homebrew 可以先 安装 Homebrew
安装组件
安装很简单,就一行: brew install pam-u2f
1 | ==> Downloading https://homebrew.bintray.com/bottles/pam-u2f-1.0.4.yosemite.bott |
(可选) 在 pam 里设置绝对路径也是可以用的,我是这么设置的。版本号不一样可以自己改下:
1 | ln -s /usr/local/Cellar/pam-u2f/1.0.4/lib/pam/pam_u2f.so /usr/lib/pam/ |
设置U2F授权
先在你的终端跑一下 pamu2fcfg
,不出意外,应该会在屏幕上打印出一个你的 用户名:hash
的东西。复制下来存到 ~/.config/Yubico/u2f_keys
中即可。
初次创建,建议使用以下操作:
1 | mkdir -p ~/.config/Yubico/ |
执行后应该看到生成的结果。
如果有多个U2F Key, 不能直接在u2f_mappings里放两行, 同一个用户名只能放一行, 按以下格式, 以 :
分隔:
1 | <username1>:<KeyHandle1>,<UserKey1>:<KeyHandle2>,<UserKey2>:... |
配置sudo U2F授权
先sudo编辑文件 /etc/pam.d/sudo
1 | # sudo: auth account password session |
在第一行之后新插入一行,可以按需填写以下内容:
1 | # 指纹授权,建议 MacBook Pro 有指纹款使用 |
以上配置效果是和gif一样的,先尝试指纹授权,如果失败则请求U2F授权,如果依旧失败则采用密码授权。配置中,指纹与U2F都是可选项目(可以跳过),但是密码授权则是在上述授权失败的情况下必须使用。
如果要改为U2F与密码同时通过才放行,可以将 sufficient
改为 required
Pro MacBook Pro Tip: have a Touch Bar with Touch ID? If you edit /etc/pam.d/sudo and add the following line to the top…
auth sufficient pam_tid.so
…you can now use your fingerprint to sudo!
Apr 17 2019, Update:
由于苹果的策略有修改,在 iTerm 中使用苹果自带的 pam_tid.so
会有兼容性问题,参考Gitlab Issue #7527。
Jul 22 2019, Update:
按照之前的说法,#7527 似乎被解决了,但是我仍在使用自己编译的版本。持续观望中(
配置锁屏 U2F授权
文件是 /etc/pam.d/screensaver
1 | # screensaver: auth account |
然后保存,测试锁屏即可。
配置登录 U2F授权
文件是 /etc/pam.d/authorization
1 | # authorization: auth account |
然后保存,重启后测试登录即可。
关于Debug
在结尾加个 debug
就可以输出调试信息了。
1 | auth sufficient pam_u2f.so debug |
参考链接
没有你们的帮助,我也写不出这么多东西,在此表示感谢
- Local Two-Factor Authentication With U2F on Ubuntu 14.04
- Github Yubico/pam-u2f
- Github skylineproject/pam-u2f-tutorial
- Debian Wiki - Security U2F
- dev.yubico pam-u2f
- U2F-Zero制造及使用手记
- Secure your OS X logins with Fido U2F Yubikey, then your Mac with Filevault Archive on archive.org
- Tweet from Cabel Sasser
- Enabling Touch ID authorization for sudo on macOS High Sierra
- biscuitehh/pam-watchid on Github
- hamzasood/pam_touchid on Github
- Reflejo/pam-touchID on Github
YubiKey4 我踩过的那些坑