关于 GPG 的基本概念,这里就不过多阐述了,没有基础知识的同学建议先学一些网络上的基础教程。要先对 GPG 有一定的基础理解。

本文中生成的 GPG 密钥仅为示例使用,本人并不使用文中的密钥,请注意鉴别。

准备工作

  1. 了解 GPG 基本概念,熟悉命令行操作
  2. 安装好虚拟机或/和下载 Tails 系统镜像
  3. 一颗追求信息安全的 ❤️

核心思想

平时只使用子密钥,将主密钥存储在受信的地方例如TPM或进行加密。

创建主密钥

首先创建一个新的 GPG 密钥对,一定要在虚拟机或者 Tails 或者你信任的电脑上操作

使用 --full-generate-key--expert 来生成密钥对,由于 RSA4096 比 ECC Curve 25519 要安全一点,这里我们的主密钥使用 RSA4096,且只用来 Certify。

rainshaw@ubuntu:~$ gpg --full-generate-key --expert
gpg (GnuPG) 2.2.19; Copyright (C) 2019 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)
   (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

选择 8 自定义用途

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

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

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

去掉 Sign 签名、Encrypt 加密用途,只保留 Certify 证明用途。

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

使用 4096位 RSA 密钥更安全,基本无法破解。

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) 
Key does not expire at all
Is this correct? (y/N) y

设置密钥的有效期,我们可以仔细的存放主密钥,这里可以选择永不过期,当然你也可以自己选择有效期,当有效期临近时,再根据需求自行决定是启用新密钥还是将旧密钥延长有效期。

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

Real name: Rainshaw
Email address: xxxx@live.com
Comment: 
You selected this USER-ID:
    "Rainshaw <xxxx@live.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o

输入你的个人信息,Comment 注释可以不填。

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: key 0xF2FE7008B02578B4 marked as ultimately trusted
gpg: directory '/home/rainshaw/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/home/rainshaw/.gnupg/openpgp-revocs.d/B5C100450331878DD5535261F2FE7008B02578B4.rev'
public and secret key created and signed.

pub   rsa4096/F2FE7008B02578B4 2021-08-16 [C]
      Key fingerprint = B5C1 0045 0331 878D D553  5261 F2FE 7008 B025 78B4
uid                              Rainshaw <xxxx@live.com>

接着输入你主密钥的密码,建议使用从未使用过的且易记的密码,长度越长越好。

如果你之前使用过 GPG,你可能有疑问,密码越长之后使用的时候不会很繁琐么,每次都要输入。不要担心,后面会教给大家怎么解决这个问题,方法是给子密钥设置与主密钥不同的密码,一方面方便日常使用,另一方面也是对主密钥的进一步保护。

格式化密钥

如果你观察仔细的话,会发现我最终打印出来的密钥格式和你可能不太一样,不要担心,这只是格式化,下面教给大家如何格式化密钥输出,懒癌福利。

不论你用什么编辑器,只需在 ~/.gnupg/gpg.conf 文件中输入以下内容即可

keyid-format long
with-fingerprint

这样之后输出密钥时就会自动打印密钥的长ID和指纹啦。

创建子密钥

刚刚生成的主密钥名为 F2FE7008B02578B4 ,它只有 [C] 用途,只能用来签发子密钥,为了满足日常使用我们需要新建三个子密钥,分别用来 Sign 签名、Encrypt 加密、Authenticate 认证。

Sign 签名密钥

使用 --expert--edit-key F2FE7008B02578B4 (将其中的密钥 id 换成你的)。

rainshaw@ubuntu:~$ gpg --expert --edit-key F2FE7008B02578B4
gpg (GnuPG) 2.2.19; Copyright (C) 2019 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.

Secret key is available.

gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
sec  rsa4096/F2FE7008B02578B4
     created: 2021-08-16  expires: never       usage: C   
     trust: ultimate      validity: ultimate
[ultimate] (1). Rainshaw <xxxx@live.com>

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

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? e

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

   (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

只保留 Sign 签名用途,并输入长度为 4096.

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 Tue 16 Aug 2022 02:52:10 AM PDT
Is this correct? (y/N) y
Really create? (y/N) y

输入子密钥有效期,建议不要选择永久有效,当子密钥临近过期或已过期时,我们可以使用主密钥对子密钥进行延长有效期,这样可以进一步保证安全。

当然这样之后会更加麻烦一些,比如你需要替换 GitHub 上的签名公钥等。

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/F2FE7008B02578B4
     created: 2021-08-16  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/0E45187E21327343
     created: 2021-08-16  expires: 2022-08-16  usage: S   
[ultimate] (1). Rainshaw <xxx@live.com>

gpg> save

最后不要忘记使用 save 命令保存。

此时再看一下现在的密钥:

rainshaw@ubuntu:~$ gpg -K
/home/rainshaw/.gnupg/pubring.kbx
---------------------------------
sec   rsa4096/F2FE7008B02578B4 2021-08-16 [C]
      Key fingerprint = B5C1 0045 0331 878D D553  5261 F2FE 7008 B025 78B4
uid                   [ultimate] Rainshaw <xxxx@live.com>
ssb   rsa4096/0E45187E21327343 2021-08-16 [S] [expires: 2022-08-16]

Encrypt 加密密钥

同样使用 --expert--edit-key F2FE7008B02578B4 (将其中的密钥 id 换成你的)。

rainshaw@ubuntu:~$ gpg --expert --edit-key F2FE7008B02578B4
gpg (GnuPG) 2.2.19; Copyright (C) 2019 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.

Secret key is available.

sec  rsa4096/F2FE7008B02578B4
     created: 2021-08-16  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/0E45187E21327343
     created: 2021-08-16  expires: 2022-08-16  usage: S   
[ultimate] (1). Rainshaw <xxxx@live.com>

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? 12
Please select which elliptic curve you want:
   (1) Curve 25519
   (3) NIST P-256
   (4) NIST P-384
   (5) NIST P-521
   (6) Brainpool P-256
   (7) Brainpool P-384
   (8) Brainpool P-512
   (9) secp256k1
Your selection? 1
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 Tue 16 Aug 2022 03:06:34 AM PDT
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/F2FE7008B02578B4
     created: 2021-08-16  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/0E45187E21327343
     created: 2021-08-16  expires: 2022-08-16  usage: S   
ssb  cv25519/7A3B9781DAD1C286
     created: 2021-08-16  expires: 2022-08-16  usage: E   
[ultimate] (1). Rainshaw <xxxx@live.com>

gpg> save

这里尝试使用 ECC 作为加密解密的算法,速度会更快一点。来源于博主 ulyc 的介绍:

Authenticate 认证密钥

认证密钥用处不多,平时我们连 SSH 使用普通的 RSA 密钥就可以了,当然如果你愿意也可以用 GPG 生成的子密钥。同样使用 --expert--edit-key F2FE7008B02578B4 (将其中的密钥 id 换成你的)。

rainshaw@ubuntu:~$ gpg --expert --edit-key F2FE7008B02578B4
gpg (GnuPG) 2.2.19; Copyright (C) 2019 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.

Secret key is available.

sec  rsa4096/F2FE7008B02578B4
     created: 2021-08-16  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/0E45187E21327343
     created: 2021-08-16  expires: 2022-08-16  usage: S   
ssb  cv25519/7A3B9781DAD1C286
     created: 2021-08-16  expires: 2022-08-16  usage: E   
[ultimate] (1). Rainshaw <xxx@live.com>

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? a

Possible actions for a 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? s

Possible actions for a RSA key: Sign Encrypt Authenticate 
Current allowed actions: 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 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 Tue 16 Aug 2022 03:12:10 AM PDT
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/F2FE7008B02578B4
     created: 2021-08-16  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/0E45187E21327343
     created: 2021-08-16  expires: 2022-08-16  usage: S   
ssb  cv25519/7A3B9781DAD1C286
     created: 2021-08-16  expires: 2022-08-16  usage: E   
ssb  rsa4096/4FFFC7DA1ABE8BE5
     created: 2021-08-16  expires: 2022-08-16  usage: A   
[ultimate] (1). Rainshaw <xxx@live.com>

gpg> save

操作大同小异,不再赘述。

导出密钥

复习下我们的核心思想,主密钥必须存储在安全的地方或被加密,子密钥用来日常使用。为了安全,我们将不会存储主密钥的明文,虽然这个明文也被我们之前设置的密码保护着,但为了更加安全,我们再套一层(有点吃饱了撑的)

刚才我们已经生成了加密密钥,所以只要加密密钥被保护好,我们就可以使用加密密钥给主密钥套一层。

导出公钥

第一种是导出所有的公钥,常用于公开到特定服务器。

rainshaw@ubuntu:~/Desktop$ gpg -ao pk.key --export F2FE7008B02578B4
rainshaw@ubuntu:~/Desktop$ cat pk.key
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBGEaMF8BEADD1NPg6UCRKEjLnVjYvMWzz26YTRWXmgux58hcFp3u+UE/o3Ji
eJiNtAGMXYWrvl1f/KhpUxQHKmZ+C30Jh1Vhk3Tu9POXDQb10BfBNtHIj3+Ei+3k

中间很长很长

XGyiVo7R4BCiGHekJk8+jqqu1YcRWlimHGgyBhYDvOg88qovp5+fBRpZWA3gRSm4
BOBkecuPQJ7tcdXz1a++eECgUBVdmvPT4yR9jEJRJSPNcVXSqw5G
=VhBK
-----END PGP PUBLIC KEY BLOCK-----

注意这个公钥仅用于示例,请不要将这个公钥和博主关联起来 (●'◡'●)

第二种是导出某个子密钥的公钥,常用于当有多个同一用途的密钥时指定密钥用。

rainshaw@ubuntu:~/Desktop$ gpg -a --export --output pk.key F2FE7008B02578B4
File 'pk.key' exists. Overwrite? (y/N) y
rainshaw@ubuntu:~/Desktop$ gpg -a --export --output pk.key F2FE7008B02578B4!
File 'pk.key' exists. Overwrite? (y/N) N
Enter new filename: pk2.key
rainshaw@ubuntu:~/Desktop$ gpg -a --export --output pk-sign.key 0E45187E21327343!
rainshaw@ubuntu:~/Desktop$ gpg -a --export --output pk-enc.key 7A3B9781DAD1C286!
rainshaw@ubuntu:~/Desktop$ gpg -a --export --output pk-auth.key 4FFFC7DA1ABE8BE5!

导出主密钥私钥

使用子加密密钥加密主密钥,注意将下面命令的 Rainshaw 改为你之前设置的名字,如果含有空格的话需要在两端加引号。

rainshaw@ubuntu:~/Desktop$ gpg -a --export-secret-key F2FE7008B02578B4 | gpg -e -r Rainshaw | dd of=sk.key
17+2 records in
17+1 records out
9095 bytes (9.1 kB, 8.9 KiB) copied, 5.82587 s, 1.6 kB/s

你可以分步执行,看看每步到底做了什么。

导出子密钥私钥

使用子加密密钥加密 Sign 和 Authenticate 密钥,注意将下面命令的 Rainshaw 改为你之前设置的名字,如果含有空格的话需要在两端加引号。同时一定要注意密钥ID一定要改,且感叹号(英文)不能省略!

rainshaw@ubuntu:~/Desktop$ gpg -a --export-secret-subkeys 0E45187E21327343! | gpg -e -r Rainshaw | dd of=sign.key
8+2 records in
8+1 records out
4508 bytes (4.5 kB, 4.4 KiB) copied, 5.10913 s, 0.9 kB/s
rainshaw@ubuntu:~/Desktop$ gpg -a --export-secret-subkeys 4FFFC7DA1ABE8BE5! | gpg -e -r Rainshaw | dd of=auth.key
7+2 records in
7+1 records out
3935 bytes (3.9 kB, 3.8 KiB) copied, 5.79333 s, 0.7 kB/s

对于加密子密钥就不能再用加密子密钥加密了(禁止套娃)。对于上面生成的这些文件,我们可以随意上传、复制,只要我们保证自己的加密子密钥是安全的即可。现在问题来了,怎么存储加密子密钥呢?

这里我也没有什么特别好的方法,我采用的是使用DES对称加密对GPG加密子密钥加密(好绕口。。。),这样能保证子密钥比不加密安全一丢丢。

rainshaw@ubuntu:~/Desktop$ gpg -a --export-secret-subkeys 7A3B9781DAD1C286! | openssl des3 -salt | dd of=enc.key
enter des-ede3-cbc encryption password:
Verifying - enter des-ede3-cbc encryption password:
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
5+1 records in
5+1 records out
2640 bytes (2.6 kB, 2.6 KiB) copied, 18.5623 s, 0.1 kB/s

过程中会先让你输入 GPG 的密码,然后再输入一个对称加密的密码,建议这两个采用不同的值(由于前者就是主密钥的密码,很复杂,所以我们可以对其稍作改动作为后者,比如更换其中的数字、符号,或者将密码顺序颠倒之类的。)

导出吊销证书

gpg: key 0xF2FE7008B02578B4 marked as ultimately trusted
gpg: directory '/home/rainshaw/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/home/rainshaw/.gnupg/openpgp-revocs.d/B5C100450331878DD5535261F2FE7008B02578B4.rev'
public and secret key created and signed.

pub   rsa4096/F2FE7008B02578B4 2021-08-16 [C]
      Key fingerprint = B5C1 0045 0331 878D D553  5261 F2FE 7008 B025 78B4
uid                              Rainshaw <xxxx@live.com>

在创建主密钥的同时,GPG 为我们生成了一个主密钥的吊销证书,位于 .gnupg/openpgp-revocs.d/ 目录下。我们需要对其更加精心备份和保护,为什么呢,因为如果你主密钥没丢,而这个吊销证书丢了,那你的主密钥就可能被攻击者吊销,这样一来,你对主密钥的精心保护还有啥用呢?这里偷个懒,使用的是之前生成的加密密钥。

rainshaw@ubuntu:~/Desktop$ cat ~/.gnupg/openpgp-revocs.d/B5C100450331878DD5535261F2FE7008B02578B4.rev | gpg -e -r Rainshaw | dd of=rev.key
2+2 records in
2+1 records out
1376 bytes (1.4 kB, 1.3 KiB) copied, 0.00671809 s, 205 kB/s

如果你追求更安全,那我建议你再生成一个 Encrypt 加密密钥,不导出它,只用它来加密这个吊销证书,这样只有在你的主密钥泄露之后,才有可能泄露这个吊销证书,而这时我们肯定是要吊销的,所以安全度很高。由于此时你的库中有两个加密密钥,导致在进行加密时会自动选择加密密钥(一般会用较新的),此时为了指定加密密钥,我们需要采用与上文不同的命令。

rainshaw@ubuntu:~/Desktop$ gpg -K
/home/rainshaw/.gnupg/pubring.kbx
---------------------------------
sec   rsa4096/F2FE7008B02578B4 2021-08-16 [C]
      Key fingerprint = B5C1 0045 0331 878D D553  5261 F2FE 7008 B025 78B4
uid                 [ultimate] Rainshaw <xxx@live.com>
ssb   rsa4096/0E45187E21327343 2021-08-16 [S] [expires: 2022-08-16]
ssb   cv25519/7A3B9781DAD1C286 2021-08-16 [E] [expires: 2022-08-16]
ssb   rsa4096/4FFFC7DA1ABE8BE5 2021-08-16 [A] [expires: 2022-08-16]
ssb   cv25519/82A36E1B874BE22B 2021-08-16 [E] [expires: 2022-08-16]

rainshaw@ubuntu:~/Desktop$ gpg -a --export --output pk-enc2.key 82A36E1B874BE22B!
rainshaw@ubuntu:~/Desktop$ cat ~/.gnupg/openpgp-revocs.d/B5C100450331878DD5535261F2FE7008B02578B4.rev | gpg -e -f pk-enc2.key | dd of=rev.key
2+2 records in
2+1 records out
1379 bytes (1.4 kB, 1.3 KiB) copied, 0.00649605 s, 212 kB/s

如果你刚刚生成了一个新的加密子密钥,记得重新导出主密钥!

rainshaw@ubuntu:~/Desktop$ gpg -a --export-secret-key F2FE7008B02578B4 | gpg -e -f pk-enc.key | dd of=sk2.key
18+3 records in
19+1 records out
9774 bytes (9.8 kB, 9.5 KiB) copied, 6.38228 s, 1.5 kB/s

注意这里使用的是第一个加密密钥!

传输文件

好,到目前为止,我们生成了几个非常安全的文件,他们可以随意分发上传(但建议还是不要随意分发),之后每次使用时只需将所需的私钥文件下载然后解密安装即可。

此时如果你使用虚拟机启动 Tails OS 作为生成密钥的系统,那你可能会发现好像无法把子密钥从虚拟机中复制出来,这就很蛋疼了,我的解决方法是在客户端电脑上开设一个 SSH Server,在 Tails 中使用 SCP 命令将文件传输出来,具体命令大家可以自行百度。如果你使用的是非虚拟机运行的 Tails OS,那你可以插入一个新的U盘存储这些文件。如果你是其他的情况,请自行解决。

销毁原文件

当你把上述文件成功传输出来之后,根据不同的情况,你可能需要销毁原始的密钥文件。如果是虚拟机,那你可以直接删除虚拟机即可,如果是 Tails 可以使用鼠标右键点击 .gnupg 文件夹然后选择 wipe 即可,如果是其他情况,请自行搜索擦除文件的方法。

导入密钥

导入密钥是十分简单的,首先对加密密钥进行解密,然后导入

rainshaw@ubuntu:~/Desktop$ openssl des3 -d -in enc.key | gpg --import
enter des-ede3-cbc decryption password:
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
gpg: key F2FE7008B02578B4: public key "Rainshaw <xxx@live.com>" imported
gpg: To migrate 'secring.gpg', with each smartcard, run: gpg --card-status
gpg: key F2FE7008B02578B4: secret key imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1

然后利用这个加密密钥对你在某个机器上所要使用的子密钥进行解密然后导入

如要导入签名密钥:

rainshaw@ubuntu:~/Desktop$ gpg -d sign.key | gpg --import
gpg: encrypted with 256-bit ECDH key, ID 7A3B9781DAD1C286, created 2021-08-16
      "Rainshaw <xxx@live.com>"
gpg: key F2FE7008B02578B4: "Rainshaw <xxx@live.com>" 1 new signature
gpg: key F2FE7008B02578B4: "Rainshaw <xxx@live.com>" 1 new subkey
gpg: To migrate 'secring.gpg', with each smartcard, run: gpg --card-status
gpg: key F2FE7008B02578B4: secret key imported
gpg: Total number processed: 1
gpg:            new subkeys: 1
gpg:         new signatures: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1

如要导入认证密钥:

rainshaw@ubuntu:~/Desktop$ gpg -d auth.key | gpg --import
gpg: encrypted with 256-bit ECDH key, ID 7A3B9781DAD1C286, created 2021-08-16
      "Rainshaw <xxx@live.com>"
gpg: key F2FE7008B02578B4: "Rainshaw <xxx@live.com>" 1 new signature
gpg: key F2FE7008B02578B4: "Rainshaw <xxx@live.com>" 1 new subkey
gpg: To migrate 'secring.gpg', with each smartcard, run: gpg --card-status
gpg: key F2FE7008B02578B4: secret key imported
gpg: Total number processed: 1
gpg:            new subkeys: 1
gpg:         new signatures: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1

修改信任级别

由于我们导入的证书并不是生成的,所以其信任级别为未知,需要修改信任级别

rainshaw@ubuntu:~/Desktop$ gpg --expert --edit-key Rainshaw
gpg (GnuPG) 2.2.19; Copyright (C) 2019 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.

Secret subkeys are available.

pub  rsa4096/F2FE7008B02578B4
     created: 2021-08-16  expires: never       usage: C   
     trust: unknown       validity: unknown
ssb  cv25519/7A3B9781DAD1C286
     created: 2021-08-16  expires: 2022-08-16  usage: E   
ssb  rsa4096/0E45187E21327343
     created: 2021-08-16  expires: 2022-08-16  usage: S   
ssb  rsa4096/4FFFC7DA1ABE8BE5
     created: 2021-08-16  expires: 2022-08-16  usage: A   
[ unknown] (1). Rainshaw <xxx@live.com>

gpg> trust
pub  rsa4096/F2FE7008B02578B4
     created: 2021-08-16  expires: never       usage: C   
     trust: unknown       validity: unknown
ssb  cv25519/7A3B9781DAD1C286
     created: 2021-08-16  expires: 2022-08-16  usage: E   
ssb  rsa4096/0E45187E21327343
     created: 2021-08-16  expires: 2022-08-16  usage: S   
ssb  rsa4096/4FFFC7DA1ABE8BE5
     created: 2021-08-16  expires: 2022-08-16  usage: A   
[ unknown] (1). Rainshaw <xxx@live.com>

Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)

  1 = I don't know or won't say
  2 = I do NOT trust
  3 = I trust marginally
  4 = I trust fully
  5 = I trust ultimately
  m = back to the main menu

Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y

pub  rsa4096/F2FE7008B02578B4
     created: 2021-08-16  expires: never       usage: C   
     trust: ultimate      validity: unknown
ssb  cv25519/7A3B9781DAD1C286
     created: 2021-08-16  expires: 2022-08-16  usage: E   
ssb  rsa4096/0E45187E21327343
     created: 2021-08-16  expires: 2022-08-16  usage: S   
ssb  rsa4096/4FFFC7DA1ABE8BE5
     created: 2021-08-16  expires: 2022-08-16  usage: A   
[ unknown] (1). Rainshaw <xxx@live.com>
Please note that the shown key validity is not necessarily correct
unless you restart the program.

gpg> quit
rainshaw@ubuntu:~/Desktop$ gpg -K
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
/home/rainshaw/.gnupg/pubring.kbx
---------------------------------
sec#  rsa4096/F2FE7008B02578B4 2021-08-16 [C]
      Key fingerprint = B5C1 0045 0331 878D D553  5261 F2FE 7008 B025 78B4
uid                 [ultimate] Rainshaw <xxx@live.com>
ssb   cv25519/7A3B9781DAD1C286 2021-08-16 [E] [expires: 2022-08-16]
ssb   rsa4096/0E45187E21327343 2021-08-16 [S] [expires: 2022-08-16]
ssb   rsa4096/4FFFC7DA1ABE8BE5 2021-08-16 [A] [expires: 2022-08-16]

修改密码

这个方法是在 GnuPG 2.2 的时候测试通过的,GnuPG 2.3 实测已无法只修改子密钥密码。

上面提到过,我们在生成主密钥时使用了一个非常非常不常用、非常非常复杂的密码,在上面的操作中,我们几乎每个操作都要输入至少一次这个密码,很不方便。这里教给大家如何修改子密钥的密码。

如果你搜索 gpg subkey change passphrase 这些关键词的话,你会看到很多帖子说 GPG 无法实现这种需求,但在我实际操作后发现,虽然给子密钥修改密码会提示错误,但实际密码是修改成功了的。

首先我们在机器上导入需要的子密钥,如上面操作就可,注意一定不要导入主密钥!!!

然后我们直接修改密码:

rainshaw@ubuntu:~/Desktop$ gpg --expert --edit-key Rainshaw
gpg (GnuPG) 2.2.19; Copyright (C) 2019 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.

Secret subkeys are available.

pub  rsa4096/F2FE7008B02578B4
     created: 2021-08-16  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  cv25519/7A3B9781DAD1C286
     created: 2021-08-16  expires: 2022-08-16  usage: E   
ssb  rsa4096/0E45187E21327343
     created: 2021-08-16  expires: 2022-08-16  usage: S   
ssb  rsa4096/4FFFC7DA1ABE8BE5
     created: 2021-08-16  expires: 2022-08-16  usage: A   
[ultimate] (1). Rainshaw <xxx@live.com>

gpg> passwd
gpg: key F2FE7008B02578B4/F2FE7008B02578B4: error changing passphrase: No secret key

gpg> save
Key not changed so no update needed.

注意到我们完成修改密码操作后虽然提示我们error changing passphrase,但你可以测试一下,密码是修改成功了的!

导入主密钥

一定不要跳着看!!否则功亏一篑!!

当你完成上面的步骤后,此时如果机器是你信任的或者有TPM芯片或智能卡设备的话,你可以选择导入主密钥:

rainshaw@ubuntu:~/Desktop$ gpg -d sk2.key | gpg --import
gpg: encrypted with 256-bit ECDH key, ID 7A3B9781DAD1C286, created 2021-08-16
      "Rainshaw <xxx@live.com>"
gpg: key F2FE7008B02578B4: "Rainshaw <xxx@live.com>" 1 new signature
gpg: key F2FE7008B02578B4: "Rainshaw <xxx@live.com>" 1 new subkey
gpg: key F2FE7008B02578B4: secret key imported
gpg: Total number processed: 1
gpg:            new subkeys: 1
gpg:         new signatures: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1
gpg:  secret keys unchanged: 1

此时你可以发现主密钥的密码和子密钥的密码是不同的,我们能够很好的保护主密钥的同时,减少我们日常使用时的麻烦。

如果有 TPM 芯片可以在 --edit-key 时,使用 keytotpm 命令,将密钥转移到 TPM 芯片上,博主未尝试,请自行测试。如果有智能卡设备如 YubiKey ,则可以使用 keytocard 命令。

常见问题与解答

  1. 是否应该上传自己的公钥到公钥服务器上?
    根据博主 ulyc 的博客,不推荐将公钥上传到公钥服务器上,除非你真的需要。该博主的博客里列举了公钥服务器的诸多问题,如滥用、投毒、签名Dos、爆破、隐私问题。
  2. 如何公布自己的公钥?
    我们应该多途径、分散的公开自己的公钥,并在信任公钥时先确认指纹是否正确。你可以将公钥发布到 GitHub、个人博客、社交软件、公开论坛中。

补充

使用PGP为git commit 签名

打开终端,运行

$ gpg -K --keyid-format=long
/Users/hubot/.gnupg/secring.gpg
------------------------------------
sec   4096R/3AA5C34371567BD2 2016-03-10 [C]
uid                          Hubot 
ssb   4096R/42B317FD4BA89E7A 2016-03-10 [S]

找到你要用的 GPG 密钥 ID,上面的这个示例中为 42B317FD4BA89E7A。然后设置 Git

$ git config --global user.signingkey 42B317FD4BA89E7A
$ git config --global commit.gpgsign true

第一行是告诉 Git 用哪个密钥进行签名,第二行是告诉 Git 每次 commit 的时候自动签名,如果不加第二行,在每次 commit 时需要增加 -S 参数才会签名。

不要忘了把公钥上传到 GitHub 。

使用PGP 进行SSH

关于服务端如何启用密钥登陆,请自行查找教程,这里只介绍如何配置本地电脑。

Win10

CMD

Git Bash

网上教程很多,我配置了好几次都没成功,最后按照下面这个步骤成功了,有很多注意事项。

这个教程说不要用 CRLF 要用 LF,在 Win10 上你可以用 vscode 来设置换行符,设置的地方在 vscode 的右下角。

首先,新建文件 ~/.gnupg/gpg.conf 内容如下,最后不要换行:

$ cat ~/.gnupg/gpg.conf
use-agent

然后创建 ~/.gnupg/gpg-agent.conf 文件,内容如下,同样最后不要换行:

$ cat ~/.gnupg/gpg-agent.conf
enable-ssh-support

然后创建 ~/.gnupg/sshcontrol 文件,内容如下,不要换行:

$ gpg -K --with-keygrip
sec   rsa2048 2019-03-21 [C] [expires: 2021-03-20]
      96F33EA7F4E0F7051D75FC208715AF32191DB135
      Keygrip = 90E08830BC1AAD225E657AD4FBE638B3D8E50C9E
uid           [ultimate] Brian Exelbierd
ssb   rsa2048 2019-03-21 [E] [expires: 2021-03-20]
      Keygrip = 5FA04ABEBFBC5089E50EDEB43198B4895BCA2136
ssb   rsa2048 2019-03-21 [A]
      Keygrip = 7710BA0643CC022B92544181FF2EAC2A290CDC0E
$ cat ~/.gnupg/sshcontrol
7710BA0643CC022B92544181FF2EAC2A290CDC0E

然后修改终端配置文件:

$ cat .bashrc

export GPG_TTY=$(tty)
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
echo UPDATESTARTUPTTY | gpg-connect-agent 1> /dev/null

重启终端。

然后重启 gpg-agent, 注意每次修改配置后,都要执行这一步(不过一般这种东西配置一次也就够了)。

$ gpg-connect-agent killagent /bye
OK closing connection

$ gpg-connect-agent /bye
gpg-connect-agent: no running gpg-agent - starting 'C:\Program Files (x86)\Gpg4win\..\GnuPG\bin\gpg-agent.exe'
gpg-connect-agent: waiting for the agent to come up ... (5s)
gpg-connect-agent: connection to agent established

然后检查你的配置是否成功:

$ ssh-add -L
# 这里会打印出你的公钥

如果打印出你的公钥,如果没有输出说明还没有配置好,如你确定是按照本教程一步步做的,那可能本教程已过时,请再搜寻。

将公钥传递到服务器端便可使用 GPG 认证密钥进行 SSH 登陆了!

Linux(以 Ubuntu 为例)

首先导入对应的私钥,然后启用 gpg-agent 接替 ssh-agent 处理 SSH 请求:

cat enable-ssh-support >> ~/.gnupg/gpg-agent.conf

然后指定我们要使用的密钥:

$ gpg -K --with-keygrip
sec   rsa2048 2019-03-21 [C] [expires: 2021-03-20]
      96F33EA7F4E0F7051D75FC208715AF32191DB135
      Keygrip = 90E08830BC1AAD225E657AD4FBE638B3D8E50C9E
uid           [ultimate] Brian Exelbierd
ssb   rsa2048 2019-03-21 [E] [expires: 2021-03-20]
      Keygrip = 5FA04ABEBFBC5089E50EDEB43198B4895BCA2136
ssb   rsa2048 2019-03-21 [A]
      Keygrip = 7710BA0643CC022B92544181FF2EAC2A290CDC0E

$ echo 7710BA0643CC022B92544181FF2EAC2A290CDC0E >> ~/.gnupg/sshcontrol

注意替换上面的 7710BA0643CC022B92544181FF2EAC2A290CDC0E 为你所要用的。然后我们修改默认的 SSH 工具:

$ cat ~/.bashrc
...
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
gpgconf --launch gpg-agent
...

也就是将这两行加入到你的终端配置文件中,接下来你需要重启终端才能生效。

此时你可以检查你的配置是否正确:

$ ssh-add -L
# 这里会打印出你的公钥

将公钥传递到服务器端便可使用 GPG 认证密钥进行 SSH 登陆了!

清理 PGP 密码缓存

Linux:

$ gpgconf --kill gpg-agent

Windows:

$ echo "RELOADAGENT" | gpg-connect-agent
最后修改:2022 年 03 月 13 日
如果觉得我的文章对你有用,请随意赞赏