如何缓存使用 gpg2 --symmetric with STDIN 的脚本中的密码?

如何缓存使用 gpg2 --symmetric with STDIN 的脚本中的密码?

我正在开发一个存储和检索秘密的计算机程序,并且需要在服务器上无需用户交互即可运行。

我已经成功设置了系统中的所有内容,用于gpg-agent检索缓存的密码,无需用户交互即可加密和解密数据gpg2 --symmetric 当我指定文件名时

但是,这要求我将数据临时存储在磁盘上,并且不加密。我不想将未加密的数据放在磁盘上。所以现在我已将其更改为使用 STDIN 和 STDOUT 而不是磁盘文件。

我正在测试的命令是:cat test.txt | gpg2 --symmetric -o test.gpg

问题是现在似乎无法缓存密码,因此每次我都必须以交互方式输入。这对我的服务器程序不起作用。

我曾尝试使用 gpg-preset-passphrase 来实现这一点。但我不知道这是否可行,因为它需要一个“KEYGRIP”。我有一个粗略的想法:也许 gpg2 会使用 --symmetric 和 STDIN 来设置默认的 keygrip。但我不知道这个想法是否会成功。

我不打算使用 --passphrase 在代码中指定密码,因为这意味着我需要在代码中存储秘密,而我不会这样做……而且这会允许任何可以访问代码的人解密文件。它还会在命令行上暴露密码。

我希望我想要实现的目标足够清晰。任何想法都值得赞赏。

答案1

我最近一直在努力解决这个问题(在 gpg 文档和 qa 网站上无休止地摸索之后)遇到了这个2015 主题提到答案:

也许我遗漏了在创建和重用时如何为对称密码选择“cache_id”。你能总结一下吗?我在代码中挖了一些,但没有弄清楚它是如何完成的。

如果我没记错的话(并且逻辑没有改变),它是一个随机选择的 8 个八位字节的盐值: https://www.rfc-editor.org/rfc/rfc4880#section-3.7.1.2

...

S2K 盐被用作缓存 ID。

您可以通过列出加密文件的数据包来获取盐值:

$ echo | gpg --list-packets ~/myencryptedfile 2>&1 | grep -o '[0-9A-F]\{16\}'

0123456789ABCDEF
  • 主导echo力量Inappropriate ioctl for device错误,绕过 pinentry 提示
  • 2>&1隐藏 stderr
  • grep -o '[0-9A-F]\{16\}'从命令输出中提取盐

现在我们有了8个八位字节的十六进制编码的盐值。

不幸的是,这个方法对我来说不起作用。所以在 gpg 代码库中摸索了一段时间后,我遇到了此片段

  memset (s2k_cacheidbuf, 0, sizeof s2k_cacheidbuf);
  *s2k_cacheidbuf = 'S';
  bin2hex (s2k->salt, 8, s2k_cacheidbuf + 1);
  s2k_cacheid = s2k_cacheidbuf;

关键是第二行前面的“S”。我没在其他地方找到这个说法。

现在就全部搞定。加密文件:

$ gpg --symmetric --passphrase-fd 3 --batch --output ~/myencryptedfile <<EOF 3<<EOF3
> myencryptedcontent
> EOF
> mypassphrase
> EOF3
  • --passphrase-fd 3:通过文件描述符 3 提供密码
  • --batch: 必填项--passphrase-fd
  • 我更喜欢通过--passphrase-fd 3stdin 提供密码和要加密的数据(<<EOF3<<EOF3),而不是最初将其写入文件cat,或将其回显到 stdin(这让我担心进程列表窥探)。文档:此处文件(EOF 语法)
    。Bash此处字符串在语法上更易于接受(例如gpg --symmetric ... ~/myencryptedfile <<<myencryptedcontent 3<<<mypassphrase),但会在 bash 历史记录中留下密码,因此在这种用例中最好避免使用它们。

获取 8 个八位字节的十六进制盐值:

$ echo | gpg --list-packets ~/myencryptedfile 2>&1 | grep -o '[0-9A-F]\{16\}'

0123456789ABCDEF

确保 gpg-agent 允许密码重置:

$ cat ~/.gnupg/gpg-agent.conf
allow-preset-passphrase

$ gpg-connect-agent reloadagent /bye  # restart the agent after changing config

预设密码:

$ /usr/local/Cellar/gnupg/2.2.17/libexec/gpg-preset-passphrase --preset S0123456789ABCDEF <<EOF
> mypassphrase
> EOF
  • 在盐前面加上“S”,因此生成的 gpg-agent cacheid 是
    --preset S0123456789ABCDEF
  • 通过 heredoc (EOF) 在 stdin 上传递密码,以避免它出现在进程列表中
  • 如果出现ERR 67108924 Not supported <GPG Agent> - no --allow-preset-passphrase,则需要添加allow-preset-passphrase~/.gnupg/gpg-agent.conf重新启动代理(参见上文)
  • 在您的系统上的位置gpg-preset-passphrase可能不同。

最后,解密文件!

$ gpg --decrypt --pinentry-mode loopback ./myencryptedfile
gpg: AES encrypted data
gpg: encrypted with 1 passphrase
myencryptedcontent

笔记:
  • 我发现gpg-preset-passphrase在我的脚本中定位很烦人,因为它没有安装在系统路径的任何地方(至少在我的安装中没有)。但是,gpg-connect-agent是的,并且可以用于相同的目的,但需要额外的步骤。您只需要在使用密码之前对其进行十六进制编码(tr -d '\n'因为 EOF 添加了换行符):
$ (tr -d '\n' | hexdump -v -e '/1 "%02X"' && echo) <<EOF
> mypassphrase
> EOF
6D7970617373706872617365

$ gpg-connect-agent <<EOF
> preset_passphrase S0123456789ABCDEF -1 6D7970617373706872617365
> EOF
OK

# now decryption should work as above
  • 我在 OSX 上运行 gnupg 版本 2.2.17,通过 homebrew 安装
$ gpg --version
gpg (GnuPG) 2.2.17
libgcrypt 1.8.5

相关内容