剖析 OpenPGP 消息,计算字节数

剖析 OpenPGP 消息,计算字节数

我正在尝试生成可以嵌入二维码的非常小的 OpenPGP 加密文件。

然而,与 OpenSSL 相比,GnuPG 似乎只需输入“a”就能产生非常大的结果:

$ echo -n a|openssl enc -aes-256-ctr|wc -c 
17
$ echo -n a|gpg --symmetric -o-|wc -c
71

通过阅读手册,可以发现其中一个区别是 OpenSSL 默认只包含一个 8 字节的标头和一个 8 字节的盐,而 GnuPG 还包含盐、校验和和压缩。关闭这些后,文件大小会减小,但仍然很大:

$ echo -n a|gpg --symmetric --compress-algo none --disable-mdc --s2k-mode 0 -o-|wc -c
35

有什么方法可以进一步优化 OpenPGP 加密消息(同时保持 AES 启用)?

答案1

虽然 GnuPG 的二进制格式相当节省空间,但它是为了灵活性而构建的,而不是为了绝对最小的消息大小(通常,实际消息远大于几个字节)。最小的“通常”OpenPGP 消息是 31 字节,但您仍然可以通过一些额外的努力将其缩减为 26 字节,这是单字节内容的最小可能的 OpenPGP v4 消息。

剖析 OpenPGP 消息,计算字节数

通过查看 RFC 4880,您可以得出消息的某个最小长度,但不能低于该长度。

让我们看一下你构建的命令的输出:

$ echo -n a|gpg --symmetric --compress-algo none --disable-mdc --s2k-mode 0 -o-|gpg --list-packets
gpg: Note: simple S2K mode (0) is strongly discouraged
    gpg: AES encrypted data
gpg: encrypted with 1 passphrase
gpg: WARNING: message was not integrity protected

第一个数据包是对称密钥加密的会话密钥包。它保存会话密钥的副本,由密码使用字符串到密钥机制加密。OpenSSL 不会执行此操作,但您不能跳过此操作,除非提供会话密钥而不是密码,单独提供会话密钥(如下所述)。此数据包大小为六个字节,由以下内容构成:

# off=0 ctb=8c tag=3 hlen=2 plen=4
:symkey enc packet: version 4, cipher 7, s2k 0, hash 10

现在,加密数据包开始。它包含:

  • 2 个字节数据包头(标签和长度)
  • 18 字节随机前缀(OpenPGP CFB 使用密码块大小的随机前缀,而不是 0 字节 IV,并重复前两个字节;AES 使用 128 位 = 16 字节作为密码块长度)
# off=6 ctb=c9 tag=9 hlen=2 plen=26 new-ctb
:encrypted data packet:
    length: 26

OpenPGP 始终将消息存储在文字数据包,它添加了一些元数据。禁用压缩至少会删除额外的压缩标头。这个数据包最终又添加了 9 个字节:

  • 2 个字节数据包头(标签和长度)
  • 1字节数据格式
  • 1 字节文件名字符串长度(值为零,后面没有文件名)
  • 4字节时间戳
  • 1字节内容
# off=26 ctb=cb tag=11 hlen=2 plen=6 new-ctb
:literal data packet:
    mode b (62), created 1503680075, name="",
    raw data: 0 bytes

总结一下:您将无法保存任何一个字节 - 除非您省略字符串到密钥的派生并直接使用会话密钥而不是密码。

省略字符串到键函数

GnuPG 允许使用--show-session-key和读取和设置会话密钥--override-session-key。读取讯息撰写篇,我实际上很惊讶有效的 OpenPGP 消息根本不需要任何定义会话密钥加密的数据包。GnuPG 确实支持这种操作,但我不敢保证其他实现也能支持,因为这是使用 OpenPGP 的一种非常深奥的方式。

   OpenPGP Message :- Encrypted Message | Signed Message |
                      Compressed Message | Literal Message.
   Encrypted Message :- Encrypted Data | ESK Sequence, Encrypted Data.
   Encrypted Data :- Symmetrically Encrypted Data Packet |
         Symmetrically Encrypted Integrity Protected Data Packet

这样做应该可以保存对称密钥加密会话密钥包的 6 个字节。

构建没有对称密钥加密会话密钥包的 OpenPGP 消息

我没有找到让 GnuPG 使用预定义会话密钥的方法。但你可以生成对称加密消息,在解密过程中提取会话密钥,然后拆分该消息。

加密消息:

$ echo -n a|gpg --symmetric --compress-algo none --disable-mdc --s2k-mode 0 -o message.gpg

提取会话密钥(将要求输入密码):

$ gpg --show-session-key 0 --decrypt message.gpg
gpg: AES encrypted data
gpg: encrypted with 1 passphrase
gpg: session key: '7:F7FBBA6E0636F890E56FBBF3283E524C'
agpg: WARNING: message was not integrity protected

将 OpenPGP 消息拆分为单个数据包:

$ gpgsplit message.gpg

该文件夹现在包含四个文件:加密文件message.gpg、未加密文件message、对称密钥加密的会话密钥包000001-003.sym_enc以及最终加密的数据包000002-009.encrypted

$ ls -l
total 16
-rw-r--r-- 1 jenserat jenserat  6 Aug 25 19:36 000001-003.sym_enc
-rw-r--r-- 1 jenserat jenserat 29 Aug 25 19:36 000002-009.encrypted
-rw-r--r-- 1 jenserat jenserat  1 Aug 25 19:04 message
-rw-r--r-- 1 jenserat jenserat 35 Aug 25 19:33 message.gpg

您还可以cat合并单个数据包文件以取回message.gpg- 这两个文件只是分开的部分message.gpg。观察文件大小,它与上面讨论的大小完全匹配(当然,文字数据包的大小包含在加密数据包中,因为gpgsplit不知道密码)!

解密单独的加密数据包

这一步相当简单:

$ gpg --override-session-key '7:F7FBBA6E0636F890E56FBBF3283E524C' --decrypt 000002-009.encrypted 
agpg: WARNING: message was not integrity protected

不要忽视a警告信息前面的输出。

警告信息的含义

GnuPG 打印了两条警告信息。

gpg:注意:强烈建议不要使用简单的 S2K 模式 (0)

这是因为简单的 S2K 模式不使用散列和盐,因此可以廉价且轻松地对密码进行暴力攻击和字典攻击。

当然,后者可以对使用相同密码加密的多个文件使用相同的会话密钥,但要注意后果。

gpg:警告:消息未受完整性保护

此警告消息表明该消息可能已被攻击者更改,而解密方却无法意识到这一事实。这是因为--disable-mdc——这当然会为文件的加密校验和节省一些字节。您可以自己尝试修改十六进制编辑器中的最后一个字节。

相关内容