2021年,用更现代的方法使用PGP

一月 14, 2025 更新: 二月 25, 2025 [网络与安全] #Git #obsidian #合集 #密码学

2021年,用更现代的方法使用PGP(上)

世界上有两种密码:一种是防止你的小妹妹偷看你的文件;另一种是防止当局阅读你的文件.

  																										—— Bruce Schneier《应用密码学》

一、PGP简介

是什么?

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

能干嘛?

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

记录片《第四公民》中 Edward Snowden 就是使用 PGP 与女记者 Laura Poitras 之间收发邮件的,以下来自电影截图:

image-20210113194002027

image-20210113194014209

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

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

1609409350928

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

具体危害可参见震惊!竟然有人在 GitHub 上冒充我的身份!

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

1609410812279

用来代替SSH

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

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

用来…

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

为什么安全?

PGP目前支持的算法

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

PGP、OpenPGP、GnuPG 和 gpg

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

OpenPGP 是在 PGP 基础上定义的开放标准,使得 PGP 技术可以由任何公司和个人实现,而不需要缴纳许可费用。标准草案指出 OpenPGP 提供的是数据完整性服务,赋予用户查看、检验、生成和写入加密信息、秘钥和签名的能力。 OpenPGP 通过加密、数字签名、压缩和 Radix-64 转换来实现这些功能。

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

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

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

历史

以下来自wiki:

菲利普·齐默曼(Philip R. Zimmermann)在1991年创造了第一个版本的PGP,其名称“Pretty Good Privacy”的灵感来自于一个名为“Ralph’s Pretty Good Grocery”的杂货店——电台主播Garrison Keillor虚构出来的一个名为Lake Wobegon的城市的一个杂货店。

软件第一个版本包含一个齐默曼自己设计的对称密钥算法,与周六夜现场的一个小品BassOmatic同名。作为一个老牌的反核能活跃分子,齐默曼为了让所有有相似倾向的人们可以安全的使用BBS并且安全存储消息和文件而创造了PGP加密。在非商业用途上是不需要授权的,无须任何费用,并且在所有的发行中附带了完整的源代码。

在2001年6月5号发表的一篇标题为”PGP 10周年” [2]的文章中,齐默曼描述了他最初开发PGP时的情景:

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

PGP在互联网上传播开来,并且在这个世界上获得了非常多的拥护者。PGP用户和支持者也包括在极权主义国家持不同政见的人们(一些给齐默曼的感人信件被发表了,其中一些在美国国会之前被包括到证据中)。在世界其它地方的公民自由意志主义支持者(参考齐默曼在各个听政会上发表的证据),以及“自由通讯”激进主义分子,他们称他们自己为加密爱好者,进行宣传和分发。

二、gpg快速开始

准备工作

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

安装

🍎Mac:
brew install gpg
🐧Linux:

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

🏁Windows:

下载地址

🌐OpenBSD:
pkg_add gnupg

生成

生成主密钥
## 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, 而且没有打印出指纹信息, 是不安全的。所以在你查看密钥时应该

比如

 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)发行版来生成主要使用的密钥, 系统自带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, 不然所有更改不会生效

2021年,用更现代的方法使用PGP(中)

世界上有两种密码:一种是防止你的小妹妹偷看你的文件;另一种是防止当局阅读你的文件.

  																										—— Bruce Schneier《应用密码学》

2021年,用更现代的方法使用PGP(中)

上篇链接:

2021年,用更现代的方法使用PGP(上)

三、安全使用和备份

准备

为了安全性,建议在一台断网的Linux或者BSD系统上生成你的密钥对。

特别推荐使用 Tails (boum.org)发行版,系统自带gpg和paperkey 等工具, 可以确保全程断网操作, 同时此系统重启会擦除所有内容,还免去了擦除密钥的麻烦。 需要工具:

备份策略

我的备份策略:

主密钥备份建议备份在一个全盘加密的U盘中,然后放在一个绝对安全的地方。

备份介质:

智能卡

主密钥不建议导入智能卡设备,因为智能卡的使用场景是随身携带,随时取用,虽然智能卡中的密钥无法导出,但是随身携带增加了丢失的风险,主密钥一旦丢失,将会非常麻烦。

如果是土豪,当然导入智能卡中更好,因为智能卡中密钥无法导出,只不过这个智能卡不能再随身携带,而同样应该在一个安全的地方, 不到万不得已不再取用, 不过这样有些浪费,也是我不推荐的理由。

网盘存储

你可以将主私钥加密后上传到网盘或其他线上服务。

PaperKey

又是断网操作,又是非对称算法,还要好好保存一份私钥,这一套流程走下来你也发现了跟数字货币的相似性,很多备份方式都可以借鉴他们的思路,比如上面网盘存储就是数字货币热钱包的思路。

冷钱包很经典的一种方式,就是将私钥写/打印在纸上, 当然我们也可以借鉴,不过若你想将这一眼望不到头的私钥抄下来, 或者打印下来,将来需要恢复的时候再一个个输入进电脑,显然不现实。

📄 [图片文字版] PGP 私钥的 ASCII Armor 文本截图:以 -----BEGIN PGP PRIVATE KEY BLOCK----- 开头,正文为整屏 base64 编码的私钥数据,即密钥导出后的文本形态。

(示例私钥的一部分,用完即废,如果是你自己的私钥,打死也不要给别人看)

比如比特币, 私钥长这样:

5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss          # 这么短很容易抄写和输入

同样是非对称算法,为什么PGP的私钥就长这么多呢?

因为他们用的算法不同,比特币默认使用的是ECDSA的 secp256k1算法, 该算法只是用来签名和认证,并不用来加密(很多科普文章会说这是加密算法,比特币的算法也可以用来加密,这是错的,比特币之所以有时被称作加密货币只是因为它依赖了密码学中的非对称算法,并不是它能加密…),PGP默认的是RSA算法,可以同时用来签名和加密,前面说过目前RSA密钥长度不小于2048 bit才安全,而且PGP导出的私钥中不止密钥信息,还包含了全部的公钥信息,UID,子密钥信息,以及设置密码时的混淆。

Linux下有一个工具叫做paperkey, 可以用来消去私钥中 冗余的公钥信息,极大减少私钥长度,然后可以将精简过的私钥(base 16格式)打印出来,或者将Raw格式转换成二维码,打印出来。

注意:

  • 既然决定用PaperKey, 还是不要使用网络打印机了,
  • paperkey这个工具在Tails新版中是默认软件
  • paperkey 手册中推荐 恢复私钥时使用OCR扫描, 而大部分OCR服务都要联网, 请注意这点。
其他

密钥只是一段信息,理论上可以放在其他任何可以存储信息的介质中,例如光盘,隐写在图片中, 写入IC卡 , 纹身, , X光片 ,背下来 等等等等,可以发挥你的想象力。

安全使用

请在你信任的电脑上使用子密钥,主密钥不到万不得已不要拿出来用,如果你将子密钥放在智能卡中使用,可以忽略这部分。

子密钥可以放在移动存储设备上直接使用,而无需拷贝/导入到电脑使用, 使用方法:

gpg --homedir [你的存储设备的路径]

四、进阶使用

使用其他算法

在为什么安全?一节中 提到PGP支持很多算法,可是前面我们生成密钥时,只见到了很少的几种。

因为gpg还有一种隐藏模式—- 专家模式,生成密钥时可以使用

gpg --expert --full-gen-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

就可以看到多出来不少选项,其中ECC算法 全称Elliptic Curve Cryptography 椭圆曲线密码算法。

上面我选了 9 , 继续

  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            # 比特币使用的算法

其中美国国家标准与技术研究院(NIST)系列椭圆曲线、Brainpool系列椭圆曲线、secp256k1都存在不同的安全风险,不建议使用。

可以发现这里少了(2), 其实这里还有一些隐藏算法没有列出,可通过如下命令查看

gpg --list-config --with-colons curve

## 输出
cfg:curve:   cv25519;ed25519;nistp256;nistp384;nistp521;brainpoolP256r1;brainpoolP384r1;brainpoolP512r1;secp256k1

上面缺少的(2)对应的就是ed25519 ,是一种签名算法,你选择 (1)时, 主密钥用来签发和签名 用的就是ed25519,自动生成的用来加密的子密钥 算法 是cv25519 。

RSA VS ECC

ECC算法比RSA的优势在于,在同等强度下,ECC的密钥长度要小的多,性能也会好一些。 RSA算法的PGP私钥 通过paperkey精简过后一般还会有50+行HEX数据,如果使用的是ECC算法生成的话, 可以减到8行以内,手抄完全没问题。

RSA算法的优势在于大部分硬件设备的兼容性比较好,比如 YubiKey 5 支持 RSA 4096,但不支持 Curve 25519。而且多数设备都对RSA算法提供了硬件加速。

PS:

查阅资料时发现 ,中文博客里被很多人抄来抄去的一句话:

ed25519 加密解密很快,生成时间短而且安全性更高,rsa 则加密解密稍慢,生成时间长,安全性没有 ed25519 高,只是 rsa 基本都是默认,所以用的人更多,但是建议转换为 ed25519, 网站软件现在基本都支持了.

这句话是 错的, ed25519是 EdDSA 算法的一种,只是一种签名算法, EdDSA 全称Edwards-curve Digital Signature Algorithm,爱德华椭圆曲线实现的数字签名算法。

上面的话多出现在替换SSH默认的RSA算法为ed25519的文章中出现。其实SSH协议中的非对称算法 并不是用来加解密信息的,而只是用来认证,正确的说法是ed25519签名和认证的速度很快。

再者, 不提密钥长度说安全性都是耍流氓 ,RSA 4096 的安全强度是要 好于 ed25519 的。

追加UID

uid 即 user id , 由三个部分组成:

UID的性质:

一般来说,操作UID只有主密钥有权限操作, 后面会提到UID可以是伪造,然后提交到服务器的。

gpg --edit-key {keyid/uid}

gpg> adduid     # 进入交互界面后 输入  help  可以查看支持的操作

## 输入你要追加的信息

使用UID的场景有哪些呢? 如果你使用一个不常用的邮箱作为你的github/gitlab账号,你主要的邮箱生成的PGP 密钥就不能用来签名了。

这时候为了保护隐私 且 只要管理一个密钥,就可以 在 Github设置里把 Keep my email addresses private勾上,将为你生成的随机邮箱添加到 uid中, 这样就只需要管理一个PGP密钥,可以同时为你不同的Git账号签名。

添加头像

跟添加UID差不多, 在gpg交互 界面 输入 addphoto 就可以, 想要查看的话 输入showphoto。

使用PGP为git commit 签名

只要你PGP的uid包含 git config 中的邮箱, 使用

git commit -S -m your commit message

即可签名。

使用PGP 进行SSH

生成 认证(A)用子密钥
gpg --expert --edit-key {keyid/uid}                                   ## 使用专家模式, 不然没有认证的选项

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
Your selection? 8                                                   ## 选8, RSA, 自定义权限

Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Encrypt                               ## 这里显示默认有Sign和Encrypt两种权限

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

Your selection? S                                                   ## 关闭Sign

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                                                   ## 关闭Encrypt

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                                                   ## 开启Authenticate

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? (2048) 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 Jan  7 11:33:54 2020 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  rsa2048/7DEFA5351BCE3C55
     created: 2019-01-07  expires: 2021-01-06  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa2048/2FCE923F8ECB63F6
     created: 2019-01-07  expires: 2021-01-06  usage: E
ssb  rsa4096/19D32A8839DCAA1F
     created: 2019-01-07  expires: 2020-01-07  usage: A
[ultimate] (1). hhhhh <h@mail.com>

gpg> save                                                            ## 保存

配置文件

编辑bashrc 文件, 将默认ssh 的agent替换为 gpg-agnet

##  ~/.bashrc

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

编辑~/.gnupg/gpg-agent.conf文件,增加:

##  ~/.gnupg/gpg-agent.conf

enable-ssh-support

接着:

❯ gpg -k --with-keygrip
/Users/root/.gnupg/pubring.kbx
-----------------------------
pub   rsa2048 2019-01-07 [SC] [expires: 2021-01-06]
      8A9FC025A44AA4824C1F4AE27DEFA5351BCE3C55
      Keygrip = BEFCCDFE36CC5442B888B8459265C68B60A4ABD2
uid           [ultimate] hhhhh <h@mail.com>
sub   rsa2048/0xF681DAEBBAB82124 2019-01-07 [E] [expires: 2021-01-06]
      Keygrip = 422922ACFD099E79863D93B93333528F225C90FC
sub   rsa2048/0x501DEDC36BD409C8 2019-01-07 [A] [expires: 2020-01-07]
      Keygrip = 999A87A51CFE82DAA494BEB42F585051307F9E33

选择你新加的带有[A]标志的那个新的子密钥的 keygrip , 即999A87A51CFE82DAA494BEB42F585051307F9E33

加入到~/.gnupg/sshcontrol文件, 运行ssh-add -l, 查看是否已经加入。

然后

gpg --export-ssh-key   {keyid}!

注意,这里不能用 uid ,不然会提示:

gpg: key "uid" not found: Unusable public key
gpg: export as ssh key failed: Unusable public key

这里只能填子密钥的 key id,这个例子里就是 0x501DEDC36BD409C8。

输出的 ssh public key 放到你的服务器上的~/.ssh/authorized_keys中, 重启shell, 就可以连接了。

2021年,用更现代的方法使用PGP(下)

公钥的发布与交换

Posted by UlyC on January 26, 2021

世界上有两种密码:一种是防止你的小妹妹偷看你的文件;另一种是防止当局阅读你的文件.

  																										—— Bruce Schneier《应用密码学》

2021年,用更现代的方法使用PGP(下)

上篇链接:

2021年,用更现代的方法使用PGP(上)2021年,用更现代的方法使用PGP(中)

PGP 公钥的 发布 与 交换

讨论公钥安全交换的中文文章比较少,而这一环是整个加密体系的重中之重。 大部分的PGP教程最后一步就是让小白用户将公钥上传,这是非常过时,且不负责任的,所以这里来详细介绍下PGP 公钥的 发布 与 交换。

原则

首先明确一点: 上传公钥到 公钥服务器 不是必要的,甚至是危险的。

如果你是新手,请不要发布你的公钥到 公钥服务器。

引子

通过前文,你已经熟悉了gpg的本地使用, 并且生成了自己的PGP 密钥对。

想象一下, 如果你生活在1980年代, 想和远方的朋友加密通信,需要先交换彼此的公钥,又没有一个统一的可信的认证机构,这时会有什么问题?

当面交换吗?当然是个办法,但是相信你不会想要将下列

1609751914957

这么长的公钥抄写到纸上,然后开车送到朋友那里,再让朋友照着这个输入他的电脑。如果中间有变动,需要重复以上过程n次。

那么还有其他办法吗?

那个时代还没有line、wechat这类即时通讯软件,而邮件提供商默认是不可靠的,不然也不会有PGP的诞生。 并且彼时https还未出现,用邮件交换PGP交换公钥显然不太安全。

你们双方都需要便捷地交换公玥, 并且确认彼此得到的公钥确实是未经篡改过的,真实有效的,就成了一个难题,这样,公钥服务器也就呼之欲出了。

公钥服务器 KeyServer

公钥服务器使得人们只需要交换他们短短的key id 或者user id就可以方便地从公钥服务器下载公钥。

历史与设计初衷

第一个KeyServer 叫做 HKP( web-based OpenPGP HTTP Keyserver Protocol) Keyserver , 诞生在上世纪90年代,是Marc Horowitz在麻省理工学习时为了他的论文而搭建的。在此之前, 虽然不是那么安全, 但是大部分人依靠电子邮件来交换公钥。

虽然服务器有了, 但开发者们担心政府会试图强迫密钥服务器运营商用政府选择的各种证书来替换证书。

所以他们做出了一个决定:公钥服务器永远不会删除信息。公钥服务器可以为现有的证书添加信息(比如可以revoke/sign或者调整expire时间),但永远永远永远不会删除证书或证书的信息。

为了达到这个目标,他们开始运行一个分布式的国际公钥服务器网络,这就是现在的KeyServer。世界各地的公钥服务器会定期相互通信,同步,比较目录。如果政府强迫公钥服务器运营商删除或修改证书,那么在比较步骤中就会被发现。残缺的公钥服务器会用完好密钥服务器目录中的内容更新自己。

任何东西都不会被删除,听起来很美好,也是解决政府审查问题的一个简单而有效的办法,可是正是这个原则后来给KeyServer带来了无穷无尽的问题。

信任网络 (Web of Trust)

好了,现在我们有了一个可以方便地上传和下载公钥的地方, 这样是不是就万事大吉了呢?

对于KeyServer 来说,任何人都可以上传公钥并声称自己是Linus, 是Zuckerberg,或是任何其他人,而KeyServer并不会去验证你是否是你所声称的人(因为KeyServer本来就没有一个中心化的运营者)。

如果你有一些密码学的基础, 那么就会知道, PGP协议依靠的非对称加密算法, 最脆弱的点就在于公钥的交换这一步。公钥交换时最容易收到中间人攻击,你一定要确定你接收到的公钥确实是你想交流的人的,由此TSL引入了CA证书认证体系,由一个可信的权威第三方来认证并颁发证书来解决身份的认证问题。 (具体可参见https系列没那么浅地谈谈HTTP与HTTPS)。

“信任一个权威的第三方” 对于最初的极具有hack精神的开发者们来说, 显然是无法接受的。

当然,你可以说下载公钥后,通过电话等手段验证下公钥的指纹(fingerprint),就可以确认正确与否。 但是想象一下, 如果你在公钥服务器找到一个声称自己Linus Torvalds 的人, 你并没有他的其他联系方式,将永远无法确定这个公钥持有者到底是谁、这个公钥可信与否 , 这样无疑是低效的,并且使整个系统沦为了熟人之间的小网络。

要知道,根据六度分隔理论(Six Degrees of Separation),世界上任何互不相识的两人,平均通过六个人就可以产生联系。 那么可以不可以这么思考, 假设我和小A见过面并检查过他的公钥,因而知道小A的公钥的的确确属于他本人,我选择信任小A。而小A同样验证了小B的证书并为小B的证书签名背书——小A的证书的持有人在此证明该证书是真实属于小B。那么我无须见亲见小B本人,也可以通过小A的背书而接受小B的证书。

如此循环下去,就形成了一张网, 这就是信任网络。

主流公钥服务器

1. SKS Keyserver Pool

当今世界最大的Key Server 池, 符合它的标准的世界各地的公钥服务器会定期相互通信,同步,比较目录,数据完全开放下载。 现在一般说起KeyServer说的就是这个。

KeyServer虽然一直是PGP的重要基础设置 ,但SKS Keyserver Pool 其实目前只有不到20台服务器,GnuPG默认用的服务器是其中的 HKPS pool,只有四台服务器。

2. Base Modern Software KeyServer

有一些KeyServer 没有用SKS的软件,运行的是更下现代和稳定的软件,比较有代表性的是 Ubuntu keyserver, 基于Hockeypuck , 这些服务器仍然会和SKS pool保持同步。

3. 独立KeyServer

这些服务器不与SKS pool同步数据,由中心化的运营者运行, 会对公钥上传者做一定的认证, 并且支持删除自己的公钥。

比较有代表性的有, keys.openpgp.org ,KeyBase等等。

在gpg中使用

发布
gpg   --send-keys  {keyid/uid}
下载
gpg --recv-keys {keyid/uid}

此时有可能报错

gpg: "xxxxxr" not a key ID: skipping

这时换个KeyServer就行:

gpg  --keyserver hkps://keyserver.ubuntu.com --recv-keys {keyid/uid}
签名和验证 他人公钥

验证公钥真实性 依靠多渠道确认的 公钥指纹。

一般来说为别人的公钥签名后,需要发还给他,或者发到公钥服务器(最好经过本人同意)。

gpg --sign-key  {keyid/uid}   # 为已经导入的 他人钥签名, 你为他签名,意味着你将为他的身份真实性背书,请谨慎
高级设置

因为并不推荐大家使用KeyServer,所以这里只列举了基础操作。

如果你决定使用KeyServer的话,可以参考 OpenPGP 最佳实践 - 密钥服务器设置你的客户端与服务器通信使用 HKPS (HKP On SSL)协议, 并定期更新从服务器下载的公钥。

安全风险和争议, 被玩坏的KeyServer

在20世纪90年代初,开发者们怀着对技术的信心和人性的希望,期望创建一个友善 、纯粹、没有审查的净土, 在当时背景下,KeyServer不能删除任何已上传的东西,听起来是美好的,设计似乎是合理的。

但是事实是,网络匿名环境中充满了不那么友好的, 甚至是恶意的使用者,在当今看来 ,KeyServer这个系统并不健壮,问题重重,许多问题已经被发现十多年,而且无望解决。

滥用

按照官方推荐, UID (User ID) 是用来存储用户信息的,应该在里面填上你的名字和邮箱,一个 GPG 帐号下可以有若干个 UID。

而其实这个UID是没有任何强制限制的,也就是说你可以在UID中放入任何东西,可以是小说片段,可以是磁力链接,可是以编码后的图像、音频或视频……

1609913349212

当上传到KeyServer时, UID限制了2k的字符 . 以至于有人写了个用KeyServer存文件的项目keyserver-fs)。

再次想象一下,你往网盘传了一个文件,共享给其他人,全世界的人都可以往这个网盘文件中添加文件,而且永远无法删除,情况会变得有多糟糕。

脆弱的KeyID

见过一些生成 PGP“靓号”的工具,就是指定你喜欢的ID规则,工具会暴力生成PGP密钥, 从中返回给你想要的密钥。

由此很容易想到,若攻击者知道了目标的KeyID, 完全可以通过工具来生成完全一样的KeyID(这就是碰撞), 并上传到KeyServer,来冒充目标。

而伪造一个KeyID有多容易呢,有研究人员借助scallion程序,使用了普通的GPU(Nvidia GeForce GTX)进行碰撞,花了4秒的时间就生成了一个指定的32 bit的 KeyID。

官方推荐公布自己的KeyID时,最少应该公布64 bit(也就是长16位16进制数啦 ),但是研究表明64 bit的KeyID也是可以 被碰撞的。

投毒

公钥服务器任何人都可以上传公钥,甚至你可以上传别人的公钥,比如你可以将自己签名过的别人的公钥,再次上传到KeyServer。

签名Dos

在WOT认证体系的设计中, 当客户端收到一份未知证书时,它应当从公钥服务器拉取所有为这张证书签过名的人的证书,逐层上溯,看看是否能够找到一张已经被用户信任的证书。如果能的话,就视信任这张为可信的。

2019年6月,有攻击者恶意向公钥服务器提交了对两个著名网友的签名背书。此事件中的受害者 Robert J. Hansen 的证书就被签名了 15000 次。因而任何人的 GPG 在尝试验证他的证书时,都会拉取 15000 个签名。而 GPG 在验证这么多签名的过程中会卡住很久。

由于被攻击的两个人在 GPG 社区中中地位很高,他们在 GPG 信任网络中处于相当核心的位置。这意味着——当你验证任意一份证书的时候,有不小的概率你会不小心拉取到他们俩的证书,然后你的 GPG 就会卡住。不但他们俩的证书没法用了,他们俩签名过的证书也都面临危险,乃至于他们俩签名过的证书所签名的证书……

而上传到KeyServer的所有东西都是不可删除的…为了解决这个问题, GnuPG 2.2.17 LWN.net 开始, 从KeyServer下载公钥时默认不再下载关联的公钥, 如果你想要感受证书DoS攻击的话,可以在设置中开启:

## ~/.gnupg/gpg.conf
keyserver-options no-self-sigs-only,no-import-clean
爆破

有个很厉害的程序媛Yegor Timoshenko(前面的SKS文件存储项目也是她的杰作),写了个工具SKS-Exploit,可以将任何人的PGP公钥损坏,变得不可导入。

这个工具同时还可以给任意人的公钥 追加伪造的UID(不是KeyID),并骗过KeyServer。

1609914787731

另外能直接让KeyServer宕机。

隐私问题

讽刺的是, 最初为了保护人们隐私而生的PGP , 却因为不能满足保护隐私的法规GDPR ,而使一个公开的 SKS 公钥服务器在欧洲处于违法状态(GDPR规定: 服务商必须提供删除个人信息的选项)。

许多新手按照教程提示的在创建PGP 密钥的时候填上了自己的真实姓名,并按照那些教程将公钥上传到了KeyServer,在人肉社工猖獗的今天,简直是个灾难。

我试着在KeyServer搜过一些博客作者留下的PGP Key, 不乏有些比较注重自身隐私的,可是他们大部分都将自己真实的名字(汉字或拼音)和邮箱一起发到了服务器上,要知道那里的数据是公开且永远不能删除的。 也有些人意识到问题之后revoke了带有真实姓名的公钥,可是仍然可以查到,并且变得更加显眼(revoke过的key会变红)。

其他发布公钥的方法

1. WKD(Web Key Directory)

WKD 的工作过程是 ,通过邮箱客户端 在域名服务器检查一个”well known” 的URL, 如果匹配到了邮箱地址对应的公钥,会使用https下载,并且不需要用户进行其他操作。用户不需要gpg 命令行等等复杂操作,让PGP加密回归单纯的邮件加密本身,用起来有些像S/MIME,但其实不一样。

比如这样一个URL: https://intevation.de/.well-known/openPGPkey/hu/g8td9rsyatrazsoiho37j9n3g5ypp34h 就对应 “aheinecke@intevation.de”这个邮箱地址。

这是通过Gpg4win的使用的一个示例,

📄 [图片文字版] WKD 自动加密演示图:Outlook(账户 wksdemo@testkolab.intevation.de)窗口上叠加 Kleopatra 证书管理器,All Certificates 列表中有一张 OpenPGP 证书:名称 WKS demo、邮箱 wksdemo@testkolab.intevation.de、Valid From 10.07.2018、Key-ID 以 17DBECAB 开头,展示 WKD 配置后证书被自动获取使用。

这种方法不会泄露自己的邮箱,也不需要验证指纹, 但是需要你的邮件服务商提供支持。

proton邮箱是原生支持 WKD的, 但是它使用的是自己私钥,似乎没办法使用使用自己本地的公钥,其他也有支持。

如果你有自己的邮箱服务器,并且想折腾的话,可以参照WKD Hosting。

2. 当面交换

如果是和熟识的朋友,你可以约在任何合适地方,用你喜欢的方式交换密钥, 比如交换纸条,交换TF卡 或者usb设备,互相签名认证,互相得到公钥。

如果是想认识更多的人,并让自己的公钥被更多的人认证, 你可以参加「公钥签名派对(Key signing party)」。参与派对的人们相互交换公钥的指纹(公钥一般是存在服务器或是一个别人可以下载到的地方,这里只交换指纹),甚至需要相互出示身份证、护照、驾照、出身证明,以验明正身。

Key signing in front of FOSDEM 2008

3. DNS

有很多种方法将你的公钥通过DNS服务发布,但是有些一些方法只适用于老版本的GnuPG,有些方法只适用与新版本的GnuPG,兼容性不佳,而且搞起来比较繁琐,有兴趣的可以自行查找资料。在这里并不推荐使用。

4.个人网站 或 社交软件中

现在中文世界, PGP的使用者中 有很多都是 独立的博客作者, 如果你拥有自己的博客或者个人网站,当然可以选择将自己的公钥发布在上面,最好给你的网站上一个Https 。

很多社交网站的个人展示页,可以自由编辑你的信息,你可以将PGP的 公钥发布在这里, 或者将 指纹 放在这, 这样通过其他渠道下载到公钥的人,也可以确认身份。

5. 代码仓库或Gist

无论你是不是开发者, 都可以拥有一个Github账号, 你可以开一个仓库专门用来发布自己的公钥,或者将公钥发布到Gist。

6. 共享笔记

Notion或者印象笔记等可以共享笔记的地方,都可以贴出你的公钥。

最后

使用分散的、多渠道的、可能是线下的方式来交换和确认公钥 ,不要相信在放一处的 公钥 和指纹。

去验证紧跟在公钥后面的指纹 , 就像你去问一个诈骗者,他是不是一个诈骗者一样无用。

如果不是当面,请至少从两个渠道进行验证,比如你从一个渠道(比如这里)得到了我的公钥 , 你想和我安全通信的话,导入前一定要从另一处(比如我公布的其他账号的简介)得到我的指纹, 验证是否一致后再进行操作。

而且每次使用前,请重复以上步骤,确保你手上的公钥是最新的。

参考链接

[1]. 用 PGP 保护代码完整性系列

[2]. The GNU Privacy Handbook

[3]. [GnuPG: 用多个sub keys保护primary keymissing idea (wordpress.com)](https://jackqqcn.wordpress.com/2008/06/22/gnupg-用多个sub-keys保护primary-key/)

[4]. PGP 自我扫盲

[5]. GPG 的正确使用姿势

[6]. 电子邮件加密指南

[7]. Short key IDs are bad news (with OpenPGP and GNU Privacy Guard)

[8]. gnupg密钥签署原理和过程 // Shell’s Home (shell909090.org)

[9]. PGP Key Server|Roll Your Own Network

[10]. Are SKS keyservers safe? Do we need them?

[11]. SKS Keyserver Network Under Attack

[12]. OpenPGP 最佳实践 - 公钥服务器

[13]. GPG SKS 同步网络被投毒事件及其影响

[14]. where-to-upload-PGP-public-key-are-keyservers-still-surviving

[15]. GPG简明介绍