# 2021年，用更现代的方法使用PGP（上）

By [ulyc](https://paragraph.com/@ulyc) · 2021-11-30

---

> 世界上有两种密码:一种是防止你的小妹妹偷看你的文件;另一种是防止当局阅读你的文件.
> 
> ​ —— Bruce Schneier《应用密码学》

一、PGP简介
-------

### 是什么？

PGP 全称是 Pretty Good Privacy，是一个被设计用来加密信息，保护隐私的**软件**。现在提到“PGP”， 基本上是说 OpenPGP 标准。

### 能干嘛？

#### 能用来加密和签名信息 并让你显得很Geek

记录片《[第四公民](https://www.imdb.com/title/tt4044364/)》中 Edward Snowden 就是使用 PGP 与女记者 Laura Poitras 之间收发邮件的，以下来自电影截图：

![image-20210113194002027](https://storage.googleapis.com/papyrus_images/e5d00b6e3f2994cd353ad392ff14d906aeb9ba114bff11aadb80a72f10f1619f.png)

image-20210113194002027

![image-20210113194014209](https://storage.googleapis.com/papyrus_images/fae071d4bc7e232679345e371c05b519cc10c24d2a91b5000f7c54a8b4922c9e.png)

image-20210113194014209

PGP能保证 一条信息是你相信的人发的，除了你俩之外别人无法解密， 而且这条消息在传送时中间没有经过任何哪怕是一个标点一个字节的修改。

#### 能用来让你的Git Commit Log更加好看 验证Git Commiter身份

![1609409350928](https://storage.googleapis.com/papyrus_images/3467e594e81cf1c9e08479dacc68e9e3a1ef8a2616d8b1fd130781c620042865.png)

1609409350928

Github判定提交者只是依靠Git 客户端设置的user.name 和 user.email 来判定身份，而不会去验证真实性，也就是说你可以在你的仓库提交记录中伪造任何人的提交记录。

![](https://storage.googleapis.com/papyrus_images/f87d65dcdc1350bcffa477e00dcc5bd558060dfa555380eeccf28cb00539c9e5.png)

具体危害可参见[震惊！竟然有人在 GitHub 上冒充我的身份！](https://blog.spencerwoo.com/2020/08/wait-this-is-not-my-commit/)

#### 能用来放在博客简介里作为身份的象征 增加联系你的安全方式

![1609410812279](https://storage.googleapis.com/papyrus_images/5f02f0010df9163721fa41fba3e64752872cc42b49cde4655ee07d81acf08c44.png)

1609410812279

#### 用来代替SSH

涌有了自己pgp key之后，就可以用 gpg-agent 来代替 OpenSSH Agent来进行 SSH操作了。不过替换了之后并不会增加SSH的安全性，额， 折腾精神不死嘛。

硬要说好处的话，大概就可以更方便地使用Yubikey(一种硬件加密智能卡)来SSH。

#### 用来…

如果有其他好玩的用法，欢迎评论或邮件告诉我。

### 为什么安全？

PGP目前支持的算法

*   非对称算法: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
    
*   对称算法: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH, CAMELLIA128, CAMELLIA192, CAMELLIA256
    
*   哈希算法: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
    
*   压缩算法: Uncompressed, ZIP, ZLIB, BZIP2
    

除非量子计算机落地，目前来说2048位的RSA加密是不可破解的。

### PGP、OpenPGP、GnuPG 和 gpg

我们需要知道PGP、OpenPGP、GnuPG 和 gpg 之间的不同：

*   PGP （“Pretty Good Privacy”） 是最初商业软件的名字
    
*   OpenPGP 是与最初 PGP 工具兼容的 IETF 标准
    
*   GnuPG （“Gnu Privacy Guard”）是实现了 OpenPGP 标准的自由软件
    
*   GnuPG 的命令行工具称为 “gpg”
    

OpenPGP 是在 PGP 基础上定义的开放标准，使得 PGP 技术可以由任何公司和个人实现，而不需要缴纳许可费用。[标准草案](https://ulyc.github.io/2021/01/13/2021%E5%B9%B4-%E7%94%A8%E6%9B%B4%E7%8E%B0%E4%BB%A3%E7%9A%84%E6%96%B9%E6%B3%95%E4%BD%BF%E7%94%A8PGP-%E4%B8%8A/http%3A//www.ietf.org/rfc/rfc4880.txt)指出 OpenPGP 提供的是数据完整性服务，赋予用户查看、检验、生成和写入加密信息、秘钥和签名的能力。 OpenPGP 通过加密、数字签名、压缩和 Radix-64 转换来实现这些功能。

GnuPG 是 OpenPGP 协议的一种完备的实现，除了按照 OpenPGP 协议提供数据加解密和签名服务之外，它还提供了完整的秘钥管理功能，并实现了协议中许多可选的加密或压缩算法。

今天，“PGP”这个词几乎被普遍用来表示开放的 OpenPGP 标准，而不是最初的商业软件，因此“PGP”和“OpenPGP”是可以互换的。

“GnuPG”和“gpg”这两个词应该仅在提及工具时使用，而不用于它们产生的输出或它们实现的 OpenPGP 功能。举例：

*   PGP（而非 GnuPG 或 GPG）密钥
    
*   PGP（而非 GnuPG 或 GPG）签名
    
*   PGP（而非 GnuPG 或 GPG）公钥服务器
    

### 历史

以下来自wiki:

[菲利普·齐默曼](https://zh.wikipedia.org/wiki/%E8%8F%B2%E5%88%A9%E6%99%AE%C2%B7%E9%BD%8A%E9%BB%98%E6%9B%BC)（Philip R. Zimmermann）在1991年创造了第一个版本的PGP，其名称“Pretty Good Privacy”的灵感来自于一个名为“Ralph’s Pretty Good Grocery”的杂货店——电台主播[Garrison Keillor](https://zh.wikipedia.org/w/index.php?title=Garrison_Keillor&action=edit&redlink=1)虚构出来的一个名为[Lake Wobegon](https://zh.wikipedia.org/w/index.php?title=Lake_Wobegon&action=edit&redlink=1)的城市的一个杂货店。

软件第一个版本包含一个齐默曼自己设计的[对称密钥算法](https://zh.wikipedia.org/wiki/%E5%AF%B9%E7%A7%B0%E5%AF%86%E9%92%A5%E7%AE%97%E6%B3%95)，与[周六夜现场](https://zh.wikipedia.org/wiki/%E5%91%A8%E5%85%AD%E5%A4%9C%E7%8F%BE%E5%A0%B4)的一个小品[BassOmatic](https://zh.wikipedia.org/w/index.php?title=BassOmatic&action=edit&redlink=1)同名。作为一个老牌的[反核能活跃分子](https://zh.wikipedia.org/w/index.php?title=%E5%8F%8D%E6%A0%B8%E8%83%BD%E6%B4%BB%E8%B7%83%E5%88%86%E5%AD%90&action=edit&redlink=1)，齐默曼为了让所有有相似倾向的人们可以安全的使用BBS并且安全存储消息和文件而创造了PGP加密。在非商业用途上是不需要授权的，无须任何费用，并且在所有的发行中附带了完整的[源代码](https://zh.wikipedia.org/wiki/%E6%BA%90%E4%BB%A3%E7%A0%81)。

在2001年6月5号发表的一篇标题为”PGP 10周年” \[[2\]](https://zh.wikipedia.org/wiki/PGP#cite_note-2)的文章中，齐默曼描述了他最初开发PGP时的情景：

> 1991年的某天，我把PGP的第一版发给我几个朋友，以便上传到互联网。我最先发给Allan Hoeltje，他把这个程序发到了Peacenet，一个针对草根政治组织–特别是“和平运动”–的ISP。当时全球政治活跃分子都能访问到Peacenet。然后我又把它上传给了Kelly Goen，他接着就把源码转发到了一个专门分发源代码的Usenet新闻组。基于我的请求，他把该Usenet权限改为了“仅限美国”。Kelly还把PGP传到了（美国）国内很多BBS上面。我记不太清刚开始在网上贴是6月5号还是6号。 说出来吓人，1991年的时候，我对Usenet新闻组确是知之甚少。我并不知道那个“仅限美国”的标签只是个“建议”作用，基本上对贴子的传播（范围）毫无影响。当时，我以为这个标签会控制这个帖子的传播范围。当时我不知道如何在新闻组发贴，甚至不明白新闻组究竟是什么。

PGP在[互联网](https://zh.wikipedia.org/wiki/%E4%BA%92%E8%81%94%E7%BD%91)上传播开来，并且在这个世界上获得了非常多的拥护者。PGP用户和支持者也包括在极权主义国家持不同政见的人们（一些给齐默曼的感人信件被发表了，其中一些在美国国会之前被包括到证据中）。在世界其它地方的[公民自由意志主义](https://zh.wikipedia.org/w/index.php?title=%E5%85%AC%E6%B0%91%E8%87%AA%E7%94%B1%E6%84%8F%E5%BF%97%E4%B8%BB%E4%B9%89&action=edit&redlink=1)支持者（参考齐默曼在各个听政会上发表的证据），以及“自由通讯”激进主义分子，他们称他们自己为[加密爱好者](https://zh.wikipedia.org/w/index.php?title=%E5%8A%A0%E5%AF%86%E7%88%B1%E5%A5%BD%E8%80%85&action=edit&redlink=1)，进行宣传和分发。

二、gpg快速开始
---------

### 准备工作

建议你先参照后面教程，在随便一台机器上练习。 等熟练操作之后，再阅读 安全使用 生成你真正主要使用的PGP密钥对 ：

### 安装

#### 🍎Mac:

#### 🐧Linux:

各发行版一般都会默认安装GnuPG。

#### 🏁Windows:

[下载地址](https://www.gnupg.org/download/)

#### 🌐OpenBSD:

### 生成

#### 生成主密钥

    # step 0 
    gpg --full-gen-key
    # 这里不推荐使用的 `gpg --gen-key`
    
    
    # step 1
    gpg (GnuPG) 2.2.20; Copyright (C) 2020 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)
      (14) Existing key from card
    Your selection?
    #  默认就可以
    
    
    # step 2
    RSA keys may be between 1024 and 4096 bits long.
    What keysize do you want? (3072)
    
    # 此处输入你希望的密钥长度， RSA的不应低于2048 bits，当然输入的数字越大越安全，相应的，加解密的速度也会更慢
    
    # step 3
    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)  2y
    
    # 默认可以选0 ，即永不过期， 这里我选了2y，  因为到期之前随时可以更改你的过期时间，以确保你对此密钥仍拥有控制权
    
    # step 4
    Key expires at Wed 11 Jan 2023 05:50:53 PM CST
    Is this correct? (y/N) y
    
    #确定
    
    # step 5
    
    GnuPG needs to construct a user ID to identify your key.
    
    Real name:  linus   # 这里名字可以是网名，可以是任意名字，如果你注重隐私就不要输入自己真名了 
    Email address: linus@outlook.com  
    Comment:     # 备注可以留空
    
    # 注意了： 这里的邮箱， 如果你不打算使用PGP为你的Git记录认证， 这里其实是可以随便输入的，不需要是你的邮箱， 甚至不需要是一个真实存在的邮箱，只要接受你信息的人知道就行。隐私泄漏问题很严重，你一旦设置了，并且发布到公钥服务器，就永远删不掉了 😅
    
    
    # step 6
    You selected this USER-ID:
        "linus <linust@outlook.com>"
    
    Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
    
    # 确认无误后输入 o
    
    # step 7
    ┌──────────────────────────────────────────────────────┐
    │ Please enter the passphrase to                       											  │
    │ protect your new key                                                           │ 
    │                                                      │
    │ Passphrase: ________________________________________ 														 │
    │                                                      │
    │       <OK>                              <Cancel>     │
    └──────────────────────────────────────────────────────┘
    
    # 输入一个复杂的密码 并确认
    
    # step 8
    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.
    
    # 随机移动你的鼠标，越随机你的密钥越安全
    
    # step 9 大功告成
                     
    gpg: key 99F583599B7E31F1 marked as ultimately trusted
    gpg: revocation certificate stored as '/root/.gnupg/openpgp-revocs.d/705358AB85366CAB05C0220F99F583599B7E31F1.rev'
    public and secret key created and signed.
    
    pub   rsa3072 2021-01-11 [SC]
          705358AB85366CAB05C0220F99F583599B7E31F1			 # 你的 key id
    uid                      linus <linus@outlook.com>
    sub   rsa3072 2021-01-11 [E] 		 # 这个是自动生成的用于加密的子密钥，E代表Encrypt 加密
    

以下是常见缩写释义：

    A    =>    Authentication
    C    =>    Certify
    E    =>    Encrypt
    S    =>    Sign
    ?    =>    Unknown capability
    sec  =>    Secret Key
    ssb  =>    Secret SuBkey
    pub  =>    Public Key
    sub  =>    Public Subkey
    

#### 生成子密钥

你日常使用应该使用子密钥，主密钥除了签发新的子密钥不要使用。

建议为不同环境，不同用途都单独生成子密钥，互不干扰。

    # step 0
    gpg --edit-key linus # 或者key id  
    
    # step 1  进入gpg交互界面	
    gpg (GnuPG) 2.2.20; Copyright (C) 2020 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  rsa3072/99F583599B7E31F1
         created: 2021-01-11  expires: never       usage: SC
         trust: ultimate      validity: ultimate
    ssb  rsa3072/6FE9C71CFED44076
         created: 2021-01-11  expires: never       usage: E
    [ultimate] (1). linus <linus@outlook.com>C
    
    # step 2  
    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)
      (14) Existing key from card
    Your selection? 4   
    # 根据你的用途选择， 这里生成一个只用于签名的子密钥（sign only）
    
    #  后面的选择和主密钥生成的大同小异，按提示操作即可
    
    # 生成完毕后
    sec  rsa3072/99F583599B7E31F1
         created: 2021-01-11  expires: never       usage: SC
         trust: ultimate      validity: ultimate
    ssb  rsa3072/6FE9C71CFED44076
         created: 2021-01-11  expires: never       usage: E
    ssb  rsa3072/FDB960B857D397F6
         created: 2021-01-11  expires: never       usage: S
    [ultimate] (1). linus <linus@outlook.com>
    
    #  last step
    gpg>  save  #  记得save， 直接退出的话什么也没有
    

#### 生成撤销证书

假如你忘了主密钥的密码，或者丢失了对主密钥的控制权（丢失，被夺取），如果没有撤销凭证的话， 除了一个个通知你的朋友们没有任何办法 证明你不再使用这个密钥，这简直是灾难。

    # step 0
    gpg --gen-revoke -ao   revoke.pgp   linus # uid 或者key id
    
    # step 1
    sec  rsa3072/99F583599B7E31F1 2021-01-11 linus <linus@outlook.com>
    
    Create a revocation certificate for this key? (y/N) y
    Please select the reason for the revocation:
      0 = No reason specified
      1 = Key has been compromised
      2 = Key is superseded
      3 = Key is no longer used
      Q = Cancel
    (Probably you want to select 1 here) 3
    
    # 按提示走完流程就可以
    

生成的`revoke.pgp`就是撤销凭证， 有了这个撤销凭证，你可以在没有密码的情况下使一个公钥失效，所以一定要妥善保存，而且最好比主密钥多一份。

### 列出密钥

    # 列出所有公钥、子公钥
    gpg --list-keys 
    # 列出所有密钥、子密钥
    gpg --list-secret-keys 
    
    # 简化命令
    gpg -k 
    gpg -K  
    
    
    # 输出 
    sec   rsa3072 2021-01-11 [SC]
          705358AB85366CAB05C0220F99F583599B7E31F1
    uid           [ultimate] linus <linus@outlook.com>
    ssb   rsa3072 2021-01-11 [E]
    ssb   rsa3072 2021-01-11 [S]
    

这样并没有列出子密钥的id, 而且没有打印出指纹信息， 是不安全的。所以在你查看密钥时应该

*   加上 `--keyid-format long`输出长ID
    
*   加上 `--fingerprint` 输出指纹信息
    

比如

     gpg --fingerprint -K --keyid-format long
     
     # 输出
    sec   rsa3072/0x99F583599B7E31F1 2021-01-11 [SC]		# 长ID
          Key fingerprint = 7053 58AB 8536 6CAB 05C0  220F 99F5 8359 9B7E 31F1 #指纹信息
    uid                   [ultimate] linus <linus@outlook.com>
    ssb   rsa3072/0x6FE9C71CFED44076 2021-01-11 [E]            # 斜杠后面的就是子密钥ID
    ssb   rsa3072/0xFDB960B857D397F6 2021-01-11 [S]
    

### 安全设置

每次都打 `--keyid-format long`和`--fingerprint` 很烦对不对， 编辑配置gpg文件, `vim ~/.gnupg/gpg.conf`

    # ~/.gnupg/gpg.conf
    
    keyid-format 0xlong
    with-fingerprint
    

### 备份

    gpg -ao public-key.txt --export linus   # 导出公钥
    
    # 注意这里最后 要带上“!”， 不然会导出全部子密钥， 感谢@Dallas Lu 指正 
    gpg  -ao secret-key --export-secret-key 99F583599B7E31F1! 			# 导出主私钥，建议secret-key 替换为你的加密设备备份文件的路径，直接导入到设备中
    gpg  -ao sign-subkey --export-secret-subkeys FDB960B857D397F6!   	 #导出有[S]标识、签名用子私钥
    gpg  -ao encrypt-subkey --export-secret-subkeys 6FE9C71CFED44076!    #导出有[E]标识、加密用子私钥 ,这里的ID替换为你的子密钥ID
    
    
    # 别忘了同时将你刚刚生成的撤销凭证也备份起来
    

### 删除

备份完后，要将本机的密钥清除干净，首先删除：

    
    gpg --delete-secret-keys linus  # 删除私钥，  UID 也可以替换成子密钥ID, 主密钥Key ID
    gpg --delete-keys linus		 # 删除公钥
    
    # 如果想全部删除推荐直接删文件夹,即删除 $HOME/.gnupg
    

由于gpg生成的私钥会在你的磁盘上使用明文储存，所以一个单独的 `rm` 或者右键删除 并不能彻底删除掉，可以使用 wipe 工具。如果你使用的是 SSD 且没有 启用全盘加密，是没法彻底删除的。

特别推荐使用 [Tails (boum.org)](https://tails.boum.org/)发行版来生成主要使用的密钥， 系统自带pgp和paper key 等工具, 可以确保全程断网操作, 同时此系统重启会擦除所有内容，还免去了擦除密钥的麻烦。

### 导入

    #从文件导入
    gpg --import [密钥文件]   # 刚刚备份的子密钥文件， 或者其他人的公钥
    
    # 暂不推荐从公钥服务器导入，具体用法会在公钥服务器一章介绍
    # 这里先推荐 练习导入自己的子密钥
    
    
     # 输出
    sec#   rsa3072/0x99F583599B7E31F1 2021-01-11 [SC]		 # sec 后面带有 # 号说明主密钥未导入，是安全的
          Key fingerprint = 7053 58AB 8536 6CAB 05C0  220F 99F5 8359 9B7E 31F1 #指纹信息
    uid                   [unknown] linus <linus@outlook.com>
    ssb #    rsa3072/0x6FE9C71CFED44076 2021-01-11 [E]           # 带有 # 号说明该子密钥已导入
    

### 签名和验证

这里只讲 如何签名和验证 他人文件， 为他人公钥签名和验证 放在公钥的发布和交换一章讲解。

    # 第一种方式，生成二进制签名文件
    
    gpg --sign input.txt  # 当然也可以加上--output参数
    
    # 第二种方式，生成ASCII格式签名
    gpg --clearsign input.txt
    
    # 第三种，签名和原文本分开（前两种的签名文件中包含了所有原文本，所以体积会比较大）
    gpg --armor --detach-sign input.txt  #不加armor生成会二进制
    
    
    
    #  验证签名文件
    gpg --verify demo.txt.asc demo.txt
    

### 加解密

    # 加密：
    
    # recipient指定接收者的公钥ID
    gpg --recipient {keyid/uid} --output encrypt.txt --encrypt input.txt
    # 也可以按喜好加上--armor选项等
    
    # 我更喜欢用 
    gpg  -se  -o  encrypt.txt  -r  {keyid/uid}   input.txt  
    # s代表签名  e代表加密
    # o是 将结果 输出到文件  encrypt.txt
    # r后面跟 接收者的 uid或者 key id， 接收者的公钥必须已经导入过
    # input.txt 是你要加密的文件
    
    
    # 解密：
    gpg --decrypt encrypt.txt --output decrypt.txt
    # 也可以
    gpg -d encrypt.txt   # 输出到终端 直接查看
    

### 发布 与 交换

公钥的交换是所有非对称加密算法的脆弱点，所谓现代的使用方式，主要体现在密钥的交换和发布上面， 之后会单独来探讨。

**阅读并理解本系列之前请不要发布你的公钥到公钥服务器**。

### 撤销

由于PGP没有提供任何将吊销信息通知其他用户的方式，他不能保证没人会使用撤销了的已经变得不安全的密钥。

你丢失的私钥仍然可以被攻击者使用，并用来解密那些没有更新你的公钥的人发送的加密消息。 revoke 子密钥并更新公钥后，若有人用老的公钥加密信息，虽然你仍然可以解密，但是攻击者同样可以，这时候是极度不安全的。

例如：如果A的私人密钥被盗，她将发出一个密钥撤销证书（key revocation certificate），但是由于这个密钥的分发是非正式的且将费大量的时间和口舌，故不能保证密钥环中每一个有A公开密钥的用户都能收到。由于A必须用她的私人密钥签名撤消的证书，所以如果A同时丢失了私人密钥，她就不能撤销密钥。密钥的撤销问题被认为是整个系统最薄弱的环节。

所以在你将密钥撤销后，请将撤销后的公钥发布到你一贯公布公钥的地方， 并尽可能通知其他人。

#### 撤销主密钥

    gpg --import gpg-linus.asc                                               # 在一台新的电脑上导入你的公钥
    gpg: key 99F583599B7E31F1: "linus <linus@outlook.com>" not changed
    gpg: Total number processed: 1
    gpg:              unchanged: 1
    
    
    
    gpg --import revoke                                                       # 导入你备份的撤销凭证，直接会导致密钥不可用
    
    
    
    gpg: key 99F583599B7E31F1: "linus <linus@outlook.com>" revocation certificate imported
    gpg: Total number processed: 1
    gpg:    new key revocations: 1
    gpg: marginals needed: 3  completes needed: 1  trust model: pgp
    gpg: depth: 0  valid:   1  signed:   1  trust: 0-, 0q, 0n, 0m, 0f, 1u
    gpg: depth: 1  valid:   1  signed:   0  trust: 1-, 0q, 0n, 0m, 0f, 0u
    gpg: next trustdb check due at 2021-09-29
    
    
    
    gpg -k																	# 查看密钥，已经revoke
    
    
    pub   rsa3072 2021-01-11 [SC] [revoked: 2021-01-11]
          705358AB85366CAB05C0220F99F583599B7E31F1
    uid           [ revoked] linus <linus@outlook.com>
    

#### 撤销子密钥

    gpg --edit-key linus  
      
    gpg >   list  # 列出你所有的子密钥
    gpg >   key  {n}  # 选择你要销毁的子密钥的 序号
    gpg >   revkey
    gpg >   save    # 退出前一定要save, 不然所有更改不会生效
    

未完待续
----

* * *

---

*Originally published on [ulyc](https://paragraph.com/@ulyc/2021-pgp)*
