如何使用Python gnupg模块1.2.5进行对称加密?
我正在尝试用Python和gnupg进行对称加密。
这段代码在我的Windows Vista电脑上可以正常运行,那个电脑上的Python gnupg模块版本是0.3.2:
import gnupg
gpg = gnupg.GPG()
data = 'the quick brown fow jumps over the laxy dog.'
passphrase='12345'
crypt = gpg.encrypt(data, recipients=None,
symmetric='AES256',
passphrase=passphrase,
armor=False)
但是当我在我的Linux电脑上运行它,那个电脑上的Python gnupg模块版本是1.2.5时,我遇到了这个错误:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
File "/usr/local/lib/python2.7/dist-packages/gnupg/gnupg.py", line 961, in encrypt
result = self._encrypt(stream, recipients, **kwargs)
TypeError: _encrypt() got multiple values for keyword argument 'recipients'
我搜索了很多次,但找不到任何相关的信息。
6 个回答
再仔细看看文档,参数不是recipient
,而是recipients
(注意是复数形式)
文档内容(我强调的部分):
对称加密(默认是False)如果指定了这个选项,就会使用对称加密。在这种情况下,recipients需要设置为None。如果设置为True,那么就会使用默认的加密算法(CAST5)。从版本0.3.5开始,你还可以指定要使用的加密算法(比如'AES256')。查看你的gpg命令行帮助,看看支持哪些对称加密算法。需要注意的是,默认的(CAST5)可能不是最好的选择。
这个应该在python-gnupg 0.3.5及以上版本中可以使用(根据需要进行调整):
import gnupg
gpg_home = "~/.gnupg"
gpg = gnupg.GPG(gnupghome=gpg_home)
data = raw_input("Enter full path of file to encrypt: ")
phrase = raw_input("Enter the passphrase to decrypt the file: ")
cipher = raw_input("Enter symmetric encryption algorithm to use: ")
savefile = data+".asc"
afile = open(data, "rb")
encrypted_ascii_data = gpg.encrypt_file(afile, None, passphrase=phrase, symmetric=cipher.upper(), output=savefile)
afile.close()
对于0.3.5之前的版本,我就不太确定了。
在当前版本的模块中,给收件人设置为"None"时,调用gpg.encrypt_file()的时候不需要写"recipients=None"。
我觉得“标准”的 python-gnupg
模块(而不是pretty-bad-protocol的模块)中的对称加密的encrypt()
调用更容易理解:
encrypted_data = gpg.encrypt(data=msg, symmetric=True, passphrase='1234', recipients=None)
我觉得这个功能可能是坏掉了(如果我错了请纠正我)。在 gnupg 0.3.6-1
版本中,针对 Python 3.x 的问题似乎已经修复了。你可能更适合使用这个版本,或者尝试一些变通的方法,比如我接下来要描述的。
这个变通方法是在 Python 中使用命令行的 gpg
,而不是使用 gnupg
模块。当然,你也可以用 gpg.encrypt_file
来做类似的事情(不过那样就没有我在下一段提到的临时存储的密码了;那可能是更好的选择)。
如果你担心密码会在任务管理器中显示出来,我是通过把密码放在一个临时文件里,然后用 cat 命令把它传给 gpg
来解决这个问题。不过,你可能还是要担心恶意软件会在合适的时机从临时文件中偷走密码。
请注意,这段代码是用 Python 3.x 写的,因为我最常用这个版本(如果你想在 2.x 中重现它,你需要学习一下临时文件是怎么工作的,还有其他一些知识)。
import subprocess, tempfile, os, shutil
def gpg_encrypt(data, passphrase, alg="AES256", hash="SHA512", compress_alg="BZIP2", compress_lvl="9", iterations="1000000"):
#This is for symmetric encryption.
with tempfile.TemporaryDirectory() as directory:
filepath=os.path.join(directory, "tmp")
with open(filepath, "w") as FILE:
FILE.write(data)
iterations=str(iterations)
compress_level="--compress-level "+compress_lvl
if compress_alg.upper()=="BZIP2":
compress_level="--bzip2-compress-level "+compress_lvl
tmp_filename="tmp"
with tempfile.TemporaryDirectory() as DIR:
tmpfilepath=os.path.join(DIR, tmp_filename)
with open(tmpfilepath, "w") as FILE:
FILE.write(passphrase)
subprocess.Popen("cat "+tmpfilepath+"|gpg --batch --yes --passphrase-fd 0 --force-mdc --s2k-mode 3 --s2k-count "+iterations+" --s2k-cipher-algo "+alg+" --s2k-digest-algo "+hash+" --compress-algo='"+compress_alg+"' --compress-level "+compress_lvl+" -ac '"+filepath+"'", stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True).communicate()[0]
result=None
with open(filepath+".asc", "r") as FILE:
result=FILE.read()
return result
def gpg_decrypt(data, passphrase):
#This is for decryption.
with tempfile.TemporaryDirectory() as directory:
filepath=os.path.join(directory, "tmp")
with open(filepath, "w") as FILE:
FILE.write(data)
decrypted=None
tmp_filename="tmp"
with tempfile.TemporaryDirectory() as DIR:
tmpfilepath=os.path.join(DIR, tmp_filename)
with open(tmpfilepath, "w") as FILE:
FILE.write(passphrase)
decrypted=subprocess.Popen("cat "+tmpfilepath+"|gpg --batch --yes --passphrase-fd 0 --output='"+filepath+".gpg"+"' '"+filepath+"'", stdin=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0]
result=None
decrypted=not "decryption failed:" in str(decrypted)
if decrypted==True:
with open(filepath+".gpg", "r") as FILE:
result=FILE.read()
return decrypted, result #If it worked, return True. If not, return False. (And, return the decrypted data.)
test=gpg_encrypt(data="This is a test!", passphrase="enter")
print("Here is the encrypted message:\n"+test)
decrypted, data=gpg_decrypt(data=test, passphrase="enter")
if decrypted:
print("Here is the decrypted message:\n"+data)
else:
print("Incorrect passphrase to decrypt the message.")
这里是加密和解密对称加密文件的代码(为了保险起见):
import subprocess, tempfile, os, shutil
def gpg_encrypt_file(filepath, passphrase, output=None, alg="AES256", hash="SHA512", compress_alg="BZIP2", compress_lvl="9", iterations="1000000"):
#This is for symmetric encryption.
filepath=filepath.replace("'", "'\\''") #This makes it so you can have apostrophes within single quotes.
iterations=str(iterations)
compress_level="--compress-level "+compress_lvl
if compress_alg.upper()=="BZIP2":
compress_level="--bzip2-compress-level "+compress_lvl
result=None
tmp_filename="tmp"
with tempfile.TemporaryDirectory() as DIR:
tmpfilepath=os.path.join(DIR, tmp_filename)
with open(tmpfilepath, "w") as FILE:
FILE.write(passphrase)
if output:
if output[0]!=os.sep and filepath[0]==os.sep:
output=os.path.join(os.path.dirname(filepath), output)
result=subprocess.Popen("cat "+tmpfilepath+"|gpg --batch --yes --passphrase-fd 0 --force-mdc --s2k-mode 3 --s2k-count "+iterations+" --s2k-cipher-algo "+alg+" --s2k-digest-algo "+hash+" --compress-algo='"+compress_alg+"' "+compress_level+" --output='"+output+"' -ac '"+filepath+"'", stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True).communicate()[0]
else:
result=subprocess.Popen("cat "+tmpfilepath+"|gpg --batch --yes --passphrase-fd 0 --force-mdc --s2k-mode 3 --s2k-count "+iterations+" --s2k-cipher-algo "+alg+" --s2k-digest-algo "+hash+" --compress-algo='"+compress_alg+"' --compress-level "+compress_lvl+" -ac '"+filepath+"'", stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True).communicate()[0]
return result.strip()
def gpg_decrypt_file(filepath, passphrase, output=None):
#This is for decryption.
filepath=filepath.replace("'", "'\\''")
result=None
tmp_filename="tmp"
with tempfile.TemporaryDirectory() as DIR:
tmpfilepath=os.path.join(DIR, tmp_filename)
with open(tmpfilepath, "w") as FILE:
FILE.write(passphrase)
if output:
if output[0]!=os.sep and filepath[0]==os.sep:
output=os.path.join(os.path.dirname(filepath), output)
result=subprocess.Popen("cat "+tmpfilepath+"|gpg --batch --yes --passphrase-fd 0 --output='"+output+"' '"+filepath+"'", stdin=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0]
else:
result=subprocess.Popen("cat "+tmpfilepath+"|gpg --batch --yes --passphrase-fd 0 '"+filepath+"'", stdin=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0]
return not "decryption failed:" in str(result) #If it worked, return True. If not, return False.
gpg_encrypt_file(filepath="test.txt", passphrase="myPassphrase", output="test.asc")
if gpg_decrypt_file(filepath="test.asc", passphrase="myPassphrase", output="output.txt"):
print("Successfully decrypted!")
else:
print("Incorrect passphrase.")
这是一个老问题,但我在谷歌搜索时碰到了这个,发现之前的回答让我不太满意。我在python-gnupg的GitHub问题中找到了真正的答案:
gpg.encrypt(data, symmetric='AES256', passphrase=passphrase, armor=False, encrypt=False)
所以,去掉 recipients=None
,然后加上 encrypt=False
。这样你的加密数据就会保存在crypt.data里。虽然这个方法不太直观,但确实有效。
(来源: https://github.com/isislovecruft/python-gnupg/issues/110)