如何使用 GnuPG 获得尽可能小的输出(对称加密)?

Daa*_*ker 7 encryption pgp gnupg 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
Run Code Online (Sandbox Code Playgroud)

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

$ echo -n a|gpg --symmetric --compress-algo none --disable-mdc --s2k-mode 0 -o-|wc -c
35
Run Code Online (Sandbox Code Playgroud)

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

Jen*_*rat 8

虽然 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
Run Code Online (Sandbox Code Playgroud)

第一个数据包是对称密钥加密的会话密钥数据包。它保存会话密钥的副本,使用字符串到密钥机制由密码短语加密。OpenSSL 不会这样做,但您不能跳过这一点,除非提供会话密钥而不是密码短语,单独提供会话密钥(下面讨论)。此数据包大小为 6 个字节,由以下内容构建:

# off=0 ctb=8c tag=3 hlen=2 plen=4
:symkey enc packet: version 4, cipher 7, s2k 0, hash 10
Run Code Online (Sandbox Code Playgroud)

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

  • 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

总结一下:您将无法再保存一个字节——除非您省略字符串到密钥的推导并直接使用会话密钥而不是密码短语。

省略字符串到键函数

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
Run Code Online (Sandbox Code Playgroud)

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

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

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

加密消息:

$ echo -n a|gpg --symmetric --compress-algo none --disable-mdc --s2k-mode 0 -o message.gpg
Run Code Online (Sandbox Code Playgroud)

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

$ 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
Run Code Online (Sandbox Code Playgroud)

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

$ gpgsplit message.gpg
Run Code Online (Sandbox Code Playgroud)

该文件夹现在包含四个文件:加密的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
Run Code Online (Sandbox Code Playgroud)

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

解密单独的加密数据包

这一步比较简单:

$ gpg --override-session-key '7:F7FBBA6E0636F890E56FBBF3283E524C' --decrypt 000002-009.encrypted 
agpg: WARNING: message was not integrity protected
Run Code Online (Sandbox Code Playgroud)

不要忽略a警告消息前面的,这是输出。

警告信息的含义

GnuPG 打印了两条警告消息。

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

这是因为简单的 S2K 模式使对密码短语的蛮力和字典攻击既便宜又容易,因为它不使用散列和盐。

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

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

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