我正试图通过POpen
通过python程序远程控制gpg
我有一个包含加密数据的文件,我想解密,修改和写回磁盘重新加密
目前,我将解密的信息存储在临时文件中(程序结束时我shred
)。然后我对该文件执行修改,然后使用一个函数对其进行重新加密,该函数将密码管道通过stdin
。
代码如下:
def encrypt(source, dest, passphrase, cipher=None):
"""Encrypts the source file.
@param source Source file, that should be encrypted.
@param dest Destination file.
@param passphrase Passphrase to be used.
@param cipher Cipher to use. If None or empty string gpg's default cipher is
used.
"""
phraseecho = Popen(("echo", passphrase), stdout=subprocess.PIPE)
gpgargs = [
"gpg",
"-c",
"--passphrase-fd", "0", # read passphrase from stdin
"--output", dest,
"--batch",
"--force-mdc"]
if not cipher is None and len(cipher) > 0:
gpgargs.extend(("--cipher-algo", cipher))
gpgargs.append(source)
encrypter = Popen(
gpgargs,
stdin=phraseecho.stdout,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = encrypter.communicate()
rc = encrypter.returncode
if not rc == 0:
raise RuntimeError(
"Calling gpg failed with return code %d: %s" % (rc, stderr))
这非常有效,但我相当确定将敏感,解密的数据存储在临时文件中是一个相当大的安全漏洞。
所以我想以某种方式重写我的加密/解密功能,使它们能够完全在内存中工作,而不会将敏感数据存储在磁盘上。
通过stdin
管道密码并为解密数据捕获stdout
,解密工作直接进行。
encrypter.stdin.write("%s\n%s" % (passphrase, message))
无法正常工作
我的下一个最佳猜测是提供某种内存文件/管道/套接字的文件描述符或任何--passphrase-fd
参数。问题是:我不知道是否存在诸如内存文件之类的东西,或者是否适用套接字,因为我从未使用它们。
有人可以帮忙或指出我更好的解决方案吗? 解决方案不一定是可移植的 - 我只使用Linux方法完全没问题。
提前致谢...
编辑:
非常感谢你们,Lars和ryran。两种解决方案完美无缺不幸的是我只能接受一个
答案 0 :(得分:2)
以下是我在Obnam中用来运行gpg的代码, 也许它对你有所帮助。
def _gpg_pipe(args, data, passphrase):
'''Pipe things through gpg.
With the right args, this can be either an encryption or a decryption
operation.
For safety, we give the passphrase to gpg via a file descriptor.
The argument list is modified to include the relevant options for that.
The data is fed to gpg via a temporary file, readable only by
the owner, to avoid congested pipes.
'''
# Open pipe for passphrase, and write it there. If passphrase is
# very long (more than 4 KiB by default), this might block. A better
# implementation would be to have a loop around select(2) to do pipe
# I/O when it can be done without blocking. Patches most welcome.
keypipe = os.pipe()
os.write(keypipe[1], passphrase + '\n')
os.close(keypipe[1])
# Actually run gpg.
argv = ['gpg', '--passphrase-fd', str(keypipe[0]), '-q', '--batch'] + args
tracing.trace('argv=%s', repr(argv))
p = subprocess.Popen(argv, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = p.communicate(data)
os.close(keypipe[0])
# Return output data, or deal with errors.
if p.returncode: # pragma: no cover
raise obnamlib.Error(err)
return out
def encrypt_symmetric(cleartext, key):
'''Encrypt data with symmetric encryption.'''
return _gpg_pipe(['-c'], cleartext, key)
def decrypt_symmetric(encrypted, key):
'''Decrypt encrypted data with symmetric encryption.'''
return _gpg_pipe(['-d'], encrypted, key)
答案 1 :(得分:2)
克里斯: 由于你有一个简单的使用os.pipe的例子,感谢Lars,我将提供Pyrite(我的gpg的GTK前端)也能做到,希望更多的代码示例更好。由于gui方面,我的用例比你的用例稍微复杂一点 - 我实际上使用字典输入和输出,我有代码用stdin作为输入和代码启动gpg,用文件作为输入启动它,以及其他并发症。
警告说,我从列表中的gpg命令行开始,就像你一样;但是,我没有使用--passphrase-fd 0
,而是通过os.pipe()
创建自定义文件描述符,以便在加载Popen()
实例之前发送密码,该实例的输入数据为stdin=subprocess.PIPE
。以下是来自pyrite的crypt_interface模块的一些相关(修改)摘录。
#!/usr/bin/env python
# Adapted excerpts from Pyrite <http://github.com/ryran/pyrite>
from subprocess import Popen, PIPE, check_output
...
# I/O dictionary obj
self.io = dict(
stdin='', # Stores input text for subprocess
stdout='', # Stores stdout stream from subprocess
stderr=0, # Stores tuple of r/w file descriptors for stderr stream
gstatus=0, # Stores tuple of r/w file descriptors for gpg-status stream
infile=0, # Input filename for subprocess
outfile=0) # Output filename for subprocess
...
cmd = ['gpg']
fd_pwd_R, fd_pwd_W = os.pipe()
os.write(fd_pwd_W, passwd)
os.close(fd_pwd_W)
cmd.append('--passphrase-fd')
cmd.append(str(fd_pwd_R))
...
# If working direct with files, setup our Popen instance with no stdin
if self.io['infile']:
self.childprocess = Popen(cmd, stdout=PIPE, stderr=self.io['stderr'][3])
# Otherwise, only difference for Popen is we need the stdin pipe
else:
self.childprocess = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=self.io['stderr'][4])
# Time to communicate! Save output for later
self.io['stdout'] = self.childprocess.communicate(input=self.io['stdin'])[0]
# Clear stdin from our dictionary asap, in case it's huge
self.io['stdin'] = ''
# Close os file descriptors
if fd_pwd_R:
os.close(fd_pwd_R)
time.sleep(0.1) # Sleep a bit to ensure everything gets read
os.close(self.io['stderr'][5])
if self.io['gstatus']:
os.close(self.io['gstatus'][6])
...
调用所有等待self.childprocess
对象具有returncode
属性且假设返回码为0
并且输入为文本(而不是文件)的函数的函数,它然后从该词典中读取gpg的标准输出并将其打印到屏幕上。
很高兴回答问题或尝试从我有限的经验中获得帮助。可以通过以下链接找到我的联系信息。
编辑:您可能还会发现a4crypt具有指导性,因为它是一个更简单的gpg前端 - 这是我为了学习python而开始的项目,后来在我“完成”之后被封存(如果有的话)这样的事情)黄铁矿。