如何使用Python gnupg模块1.2.5进行对称加密?

5 投票
6 回答
8371 浏览
提问于 2025-04-18 05:20

我正在尝试用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 个回答

0

再仔细看看文档,参数不是recipient,而是recipients(注意是复数形式)

文档内容(我强调的部分):

对称加密(默认是False)如果指定了这个选项,就会使用对称加密。在这种情况下,recipients需要设置为None。如果设置为True,那么就会使用默认的加密算法(CAST5)。从版本0.3.5开始,你还可以指定要使用的加密算法(比如'AES256')。查看你的gpg命令行帮助,看看支持哪些对称加密算法。需要注意的是,默认的(CAST5)可能不是最好的选择。

0

这个应该在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"。

1

我觉得“标准”的 python-gnupg 模块(而不是pretty-bad-protocol的模块)中的对称加密的encrypt()调用更容易理解:

encrypted_data = gpg.encrypt(data=msg, symmetric=True, passphrase='1234', recipients=None)

详细信息可以查看:https://stackoverflow.com/a/72355824/605356

2

我觉得这个功能可能是坏掉了(如果我错了请纠正我)。在 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.")
9

这是一个老问题,但我在谷歌搜索时碰到了这个,发现之前的回答让我不太满意。我在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)

撰写回答