I think it's just broken (correct me if I'm wrong). It appears to be fixed in gnupg 0.3.6-1
for Python 3.x. You're probably better off using that or trying a workaround, such as one similar to what I'm about to describe.
This workaround uses the command-line gpg
in Python instead of the gnupg
module. You could do a similar workaround with gpg.encrypt_file
, of course (which wouldn't have the temporarily stored passphrase I talk about in the next paragraph; that would most likely be a better choice).
If you're concerned about the passphrase showing up in the task manager, I took care of that by putting the password in a temporary file and using cat to pipe it into gpg
. However, you might have to worry about malware snatching the password from the temporary file, if it can do it in time.
Note that this code is in Python 3.x, since that's what I use the most (to recreate it in 2.x you'll have to learn how temporary files work in it, and maybe a few other things).
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.")
Here's the code for encrypting/decrypting symmetrically encrypted files (just for good measure):
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.")