Yubikey Pointer

YubiKey 是由 Yubico 生产的身份认证设备,支持一次性密码(OTP)、公钥加密和身份认证,以及由FIDO联盟(FIDO U2F)开发的通用第二因素(U2F)等协议。

本文记录了自己在使用 YubiKey 时的一些操作,仅供参考。

因涉及到密钥等相关操作,且多为不可逆操作,数据无价,三思后行。

准备工作

首先需要安装以下软件:

YubiKey Manager

是 Yubico 官方出品的 YubiKey Python 库及基于 CLI 的配置管理软件。

GnuPG

RFC4880(也称为PGP)定义的 OpenPGP 标准的免费完整实现。

PCSClite

使用 SCard API(PC/SC)访问智能卡的中间库。

CCID

通用 USB 芯片/智能卡接口设备驱动程序。

sudo pacman -S yubikey-manager gnupg pcsclite ccid

配置 OpenPGP

生成 GPG 密钥

准备工作

创建临时工作目录

export GNUPGHOME=$(mktemp -d -t gnupg_$(date +%Y%m%d%H%M)_XXX)

下载加固后的 GPG 配置

wget -O $GNUPGHOME/gpg.conf https://raw.githubusercontent.com/drduh/config/master/gpg.conf

之后生成一个用来保护 GPG 密钥的强密码然后你可以把它记住或者写下来。

gpg --gen-random --armor 0 24
之后的步骤中我们将会被多次要求输入密码进行认证,请保持它可以被方便的访问到。

创建主密钥

首先我们来创建主密钥。 主密钥只用来进行证书签发:其签发的子密钥被用来加密,签名及认证。

注意任何时刻主密钥都应该保持离线,并且只在用来撤销或签发新的子密钥时进行访问。 密钥也可以由 YubiKey 自身生成从而保证不会有任何其它副本存在。

首先生成主密钥

gpg --expert --full-generate-key
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)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
   (9) ECC and ECC
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (13) Existing key
  (14) Existing key from card
Your selection? 8 (1)

Possible actions for a RSA key: Sign Certify Encrypt Authenticate
Current allowed actions: Sign Certify Encrypt

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

Your selection? S (2)

Possible actions for a RSA key: Sign Certify Encrypt Authenticate
Current allowed actions: Certify Encrypt

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

Your selection? E (3)

Possible actions for a RSA key: Sign Certify Encrypt Authenticate
Current allowed actions: Certify

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

Your selection? Q (4)
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096 (5)
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 (6)
Key does not expire at all
Is this correct? (y/N) y (7)

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

Real name: WangHeng (8)
Email address: admin@eastack.me (9)
Comment:
You selected this USER-ID:
    "WangHeng <admin@eastack.me>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O (10)
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: /tmp/gnupg_202202101443_BI8/trustdb.gpg: trustdb created
gpg: key 0xB23B3BF3A6CB4BC5 marked as ultimately trusted
gpg: directory '/tmp/gnupg_202202101443_BI8/openpgp-revocs.d' created
gpg: revocation certificate stored as '/tmp/gnupg_202202101443_BI8/openpgp-revocs.d/46ACAD1C5FF241EA70F251CFB23B3BF3A6CB4BC5.rev'
public and secret key created and signed.

pub   rsa4096/0xB23B3BF3A6CB4BC5 2022-02-10 [C]
      Key fingerprint = 46AC AD1C 5FF2 41EA 70F2  51CF B23B 3BF3 A6CB 4BC5
uid                              WangHeng <admin@eastack.me>
1 这里输入 8 使用 RSA(自定义所需功能)
2 这里输入 S 关闭签名功能
3 这里输入 E 关闭加密功能
4 这里输入 Q 完成功能配置
5 这里输入 4096 我们使用 4096 位的密钥长度
6 这里输入 0 让主证书永不过期
7 这里输入 y 确认证书过期配置
8 这里输入真实姓名 WangHeng
9 这里输入邮件地址 admin@eastack.me
10 最后输入 O 确认用户 ID
将证书保存在持久且安全的地方,因为它会在证书过期后用来签发新的子证书和为其他 YubiKey 提供密钥。

将密钥 ID 暴露为一个变量方便之后使用

export KEYID=0xB23B3BF3A6CB4BC5

创建子证书

下面我们通过编辑主证书来为其添加子证书:

gpg --expert --edit-key $KEYID
Secret key is available.

sec  rsa4096/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
[ultimate] (1). WangHeng <admin@eastack.me>

gpg>
创建签名证书
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? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 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 2023年02月10日 星期五 15时15分18秒 CST
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/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x1F273CFBE45BF998
     created: 2022-02-10  expires: 2023-02-10  usage: S
[ultimate] (1). WangHeng <admin@eastack.me>

gpg>
创建加密证书
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? 6
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 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 2023年02月10日 星期五 15时16分17秒 CST
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/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x1F273CFBE45BF998
     created: 2022-02-10  expires: 2023-02-10  usage: S
ssb  rsa4096/0x0151A283A717FE5B
     created: 2022-02-10  expires: 2023-02-10  usage: E
[ultimate] (1). WangHeng <admin@eastack.me>

gpg>
创建认证证书
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? 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? (3072) 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 2023年02月10日 星期五 15时17分47秒 CST
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/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x7A4D04F73FD5999C
     created: 2022-02-10  expires: 2023-02-10  usage: A
[ultimate] (1). WangHeng <admin@eastack.me>

gpg>

最后保存对密钥的修改并退出

gpg> save

备份 GPG 密钥

TODO

转移 GPG 密钥

使用 keytocard 转移密钥到 YubiKey 中是不可逆的单向操作。 确保你在操作前进行了备份: keytocard 将本地磁盘上的密钥转换为存根, 这意味这磁盘上的副本不能再传输到之后的安全密钥设备中或生成新的证书。

当前选中的密钥使用 * 标识标记,在移动时一次只能选中一个密钥。

gpg --edit-key $KEYID
Secret key is available.

sec  rsa4096/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x83641F51609171F7
     created: 2022-02-10  expires: 2023-02-10  usage: S
ssb  rsa4096/0xCAE6AFDCEC685C3B
     created: 2022-02-10  expires: 2023-02-10  usage: E
ssb  rsa4096/0x7A4D04F73FD5999C
     created: 2022-02-10  expires: 2023-02-10  usage: A
[ultimate] (1). WangHeng <admin@eastack.me>

gpg>

转移签名密钥

gpg> key 1 (1)

sec  rsa4096/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb* rsa4096/0x83641F51609171F7
     created: 2022-02-10  expires: 2023-02-10  usage: S
ssb  rsa4096/0xCAE6AFDCEC685C3B
     created: 2022-02-10  expires: 2023-02-10  usage: E
ssb  rsa4096/0x7A4D04F73FD5999C
     created: 2022-02-10  expires: 2023-02-10  usage: A
[ultimate] (1). WangHeng <admin@eastack.me>

gpg> keytocard (2)
Please select where to store the key:
   (1) Signature key
   (3) Authentication key
Your selection? 1 (3)

sec  rsa4096/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb* rsa4096/0x83641F51609171F7
     created: 2022-02-10  expires: 2023-02-10  usage: S
ssb  rsa4096/0xCAE6AFDCEC685C3B
     created: 2022-02-10  expires: 2023-02-10  usage: E
ssb  rsa4096/0x7A4D04F73FD5999C
     created: 2022-02-10  expires: 2023-02-10  usage: A
[ultimate] (1). WangHeng <admin@eastack.me>

gpg>
1 输入 key 1 选中签名密钥
2 输入 keytocard 将密钥转移到 YubiKey 中
3 这里提示我们输入要将密钥存储在什么位置,我们输入 1 将密钥存储在签名密钥中。

之后根据提示输入 GPG 密钥密码然后输入 YubiKey 的 Admin PIN 即可完成密钥转移。

转移加密密钥

gpg> key 1 (1)

sec  rsa4096/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x83641F51609171F7
     created: 2022-02-10  expires: 2023-02-10  usage: S
ssb  rsa4096/0xCAE6AFDCEC685C3B
     created: 2022-02-10  expires: 2023-02-10  usage: E
ssb  rsa4096/0x7A4D04F73FD5999C
     created: 2022-02-10  expires: 2023-02-10  usage: A
[ultimate] (1). WangHeng <admin@eastack.me>

gpg> key 2 (2)

sec  rsa4096/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x83641F51609171F7
     created: 2022-02-10  expires: 2023-02-10  usage: S
ssb* rsa4096/0xCAE6AFDCEC685C3B
     created: 2022-02-10  expires: 2023-02-10  usage: E
ssb  rsa4096/0x7A4D04F73FD5999C
     created: 2022-02-10  expires: 2023-02-10  usage: A
[ultimate] (1). WangHeng <admin@eastack.me>

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

sec  rsa4096/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x83641F51609171F7
     created: 2022-02-10  expires: 2023-02-10  usage: S
ssb* rsa4096/0xCAE6AFDCEC685C3B
     created: 2022-02-10  expires: 2023-02-10  usage: E
ssb  rsa4096/0x7A4D04F73FD5999C
     created: 2022-02-10  expires: 2023-02-10  usage: A
[ultimate] (1). WangHeng <admin@eastack.me>

gpg>
1 再次输入 key 1 取消选中签名密钥
2 输入 key 2 选中加密密钥

转移认证密钥

gpg> key 2

sec  rsa4096/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x83641F51609171F7
     created: 2022-02-10  expires: 2023-02-10  usage: S
ssb  rsa4096/0xCAE6AFDCEC685C3B
     created: 2022-02-10  expires: 2023-02-10  usage: E
ssb  rsa4096/0x7A4D04F73FD5999C
     created: 2022-02-10  expires: 2023-02-10  usage: A
[ultimate] (1). WangHeng <admin@eastack.me>

gpg> key 3

sec  rsa4096/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x83641F51609171F7
     created: 2022-02-10  expires: 2023-02-10  usage: S
ssb  rsa4096/0xCAE6AFDCEC685C3B
     created: 2022-02-10  expires: 2023-02-10  usage: E
ssb* rsa4096/0x7A4D04F73FD5999C
     created: 2022-02-10  expires: 2023-02-10  usage: A
[ultimate] (1). WangHeng <admin@eastack.me>

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

sec  rsa4096/0xB23B3BF3A6CB4BC5
     created: 2022-02-10  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x83641F51609171F7
     created: 2022-02-10  expires: 2023-02-10  usage: S
ssb  rsa4096/0xCAE6AFDCEC685C3B
     created: 2022-02-10  expires: 2023-02-10  usage: E
ssb* rsa4096/0x7A4D04F73FD5999C
     created: 2022-02-10  expires: 2023-02-10  usage: A
[ultimate] (1). WangHeng <admin@eastack.me>

gpg>

最后保存对密钥的修改并退出

gpg> save

验证 GPG 密钥

我们可以通过是否有 ssb> 标识来验证子密钥是否已成功转移到 YubiKey 中

gpg -K
/tmp/gnupg_202202101443_BI8/pubring.kbx
---------------------------------------
sec   rsa4096/0xB23B3BF3A6CB4BC5 2022-02-10 [C]
      Key fingerprint = 46AC AD1C 5FF2 41EA 70F2  51CF B23B 3BF3 A6CB 4BC5
uid                   [ultimate] WangHeng <admin@eastack.me>
ssb>  rsa4096/0x83641F51609171F7 2022-02-10 [S] [expires: 2023-02-10]
ssb>  rsa4096/0xCAE6AFDCEC685C3B 2022-02-10 [E] [expires: 2023-02-10]
ssb>  rsa4096/0x7A4D04F73FD5999C 2022-02-10 [A] [expires: 2023-02-10]

配置 SSH

在写这篇文章的时候 GitHub 已经于 2021年5月10日 宣布支持将使用 U2F 及 FIDO2 的安全密钥用于 SSH。 这让你使用 YubiKey 保护你的所有 GitHub 请求变得前所未有的简单, 这让你的 SSH 密钥可以在更安全的同时仍旧拥有极佳的用户体验。

快速开始

首先你需要已经安装了 libfido2OpenSSH 8.2 及之后的版本。

首先我们需要生成一个密钥对。 插入你的安全密钥然后执行下面的命令:

ssh-keygen -t ecdsa-sk

-t ecdsa-sk 选项使 OpenSSH 在 FIDO 安全密钥中创建 ECDSA 密钥,而不是传统的私钥文件。 你也可以使用 -t ed25519-sk 来创建一个 EdDSA 密钥,但并不是所有安全密钥都支持。

推荐使用 Ed25519 加密算法,因为 ECDSA 的椭圆曲线参数是 NIST 给的,一直被怀疑有后门。

这会在你的 SSH 目录中创建两个文件。 一个是 id_ecdsa_sk.pub ,这是一个普通的 OpenSSH 公钥文件。 另一个是 id_ecdsa_sk 通常其包含对应的私钥,但现在这种情况下它包含一个 key handle 其引用到安全密钥。 当要使用 SSH 密钥时你需要将 id_ecdsa_sk 复制到每个电脑上。 或者如果你的安全密钥支持的话,你可以使用 FIDO2 驻留密钥。

使用驻留密钥

如果你的安全密钥支持 FIDO2 驻留密钥,比如 YubiKey 5 Series, YubiKey 5 FIPS Series 或者 Security Key NFC by Yubico 你可以在创建 SSH 密钥的时候开启这个功能:

ssh-keygen -t ecdsa-sk -O resident

这与之前所作的相同,除了常驻密钥可以更方便的导入到新电脑里,因为可以直接在安全密钥中加载密钥。 要在新电脑上使用 SSH 密钥,确保你已经启动了 ssh-agent 然后执行以下命令:

ssh-add -K

这将加载一个 “key handle” 到 SSH 代理中并使此密钥可以在这台电脑上使用。 这对临时使用很合适,但这不是持久化的 —— 如果你重启了电脑你需要重新运行 ssh-add。 为了永久导入密钥,可以执行:

ssh-keygen -K

这会向当前目录下写入两个文件: id_ecdsa_sk_rkid_ecdsa_sk_rk.pub 。 现在你只需要重命名私钥文件为 id_ecdsa_sk 并移动到你的 SSH 目录下就可以了:

mv id_ecdsa_sk_rk ~/.ssh/id_ecdsa_sk

重置 YubiKey

在进行操作前为防止未知意外发生,可以对操作应用先进行重置操作。

如果你没有相应的备份重置后你将会永久丢失你的密钥。

可以通过以下命令查看安全密钥的相关信息。

ykman info
Device type: YubiKey 5 NFC
Serial number: ********
Firmware version: 5.*.*
Form factor: Keychain (USB-A)
Enabled USB interfaces: OTP, FIDO, CCID
NFC transport is enabled.

Applications	USB          	NFC
FIDO2       	Enabled      	Enabled
OTP         	Enabled      	Enabled
FIDO U2F    	Enabled      	Enabled
OATH        	Enabled      	Enabled
YubiHSM Auth	Not available	Not available
OpenPGP     	Enabled      	Enabled
PIV         	Enabled      	Enabled

YubiKey 5 系列和 YubiKey 5 FIPS 系列的各种应用是相互隔离的,因此需要单独进行重置。

  • FIDO2

    使用以下命令对 FIDO2 应用进行重置
    ykman fido reset
    重置 FIDO 应用时 FIDO U2F 应用也会被重置。
    然后输入 y 确认重置
    WARNING! This will delete all FIDO credentials, including FIDO U2F credentials, and restore factory settings. Proceed? [y/N]: y
    最后根据提示重新插拔 YubiKey 后轻触 YubiKey 完成重置
    Remove and re-insert your YubiKey to perform the reset...
      Touch your YubiKey...
  • OTP

    我使用的是YubiKey 5 NFC 所以我需要清除两个槽(1 和 2)中的盐。
    使用以下命令删除相应槽中的盐
    ykman otp delete 1
    然后输入 y 确认即可完成删除
    Do you really want to delete the configuration of slot 1? [y/N]: y
    Deleting the configuration in slot 1...
  • OATH

    使用以下命令对 OATH 应用进行重置
    ykman oath reset
    然后输入 y 确认即可完成重置
    WARNING! This will delete all stored OATH accounts and restore factory settings. Proceed? [y/N]: y
    Resetting OATH data...
    Success! All OATH accounts have been deleted from the YubiKey.
  • OpenPGP

    使用以下命令对 OpenPGP 应用进行重置
    ykman openpgp reset
    然后输入 y 确认即可完成重置
    WARNING! This will delete all stored OpenPGP keys and data and restore factory settings? [y/N]: y
    Resetting OpenPGP data, don't remove the YubiKey...
    Success! All data has been cleared and default PINs are set.
    PIN:         123456
    Reset code:  NOT SET
    Admin PIN:   12345678
  • PIV

    使用以下命令对 PIV 应用进行重置
    ykman piv reset
    然后输入 y 确认即可完成重置
    WARNING! This will delete all stored PIV data and restore factory settings. Proceed? [y/N]: y
    Resetting PIV data...
    Success! All PIV data have been cleared from the YubiKey.
    Your YubiKey now has the default PIN, PUK and Management Key:
    	PIN:	123456
    	PUK:	12345678
    	Management Key:	************************************************

参考链接