How to do PGP in Python (generate keys, encrypt/decrypt)
Solution 1
You don't need PyCrypto
or PyMe
, fine though those packages may be - you will have all kinds of problems building under Windows. Instead, why not avoid the rabbit-holes and do what I did? Use gnupg 1.4.9
. You don't need to do a full installation on end-user machines - just gpg.exe
and iconv.dll
from the distribution are sufficient, and you just need to have them somewhere in the path or accessed from your Python code using a full pathname. No changes to the registry are needed, and everything (executables and data files) can be confined to a single folder if you want.
There's a module GPG.py
which was originally written by Andrew Kuchling, improved by Richard Jones and improved further by Steve Traugott. It's available here, but as-is it's not suitable for Windows because it uses os.fork()
. Although originally part of PyCrypto
, it is completely independent of the other parts of PyCrypto
and needs only gpg.exe/iconv.dll in order to work.
I have a version (gnupg.py
) derived from Traugott's GPG.py
, which uses the subprocess
module. It works fine under Windows, at least for my purposes - I use it to do the following:
- Key management - generation, listing, export etc.
- Import keys from an external source (e.g. public keys received from a partner company)
- Encrypt and decrypt data
- Sign and verify signatures
The module I've got is not ideal to show right now, because it includes some other stuff which shouldn't be there - which means I can't release it as-is at the moment. At some point, perhaps in the next couple of weeks, I hope to be able to tidy it up, add some more unit tests (I don't have any unit tests for sign/verify, for example) and release it (either under the original PyCrypto
licence or a similar commercial-friendly license). If you can't wait, go with Traugott's module and modify it yourself - it wasn't too much work to make it work with the subprocess
module.
This approach was a lot less painful than the others (e.g. SWIG
-based solutions, or solutions which require building with MinGW
/MSYS
), which I considered and experimented with. I've used the same (gpg.exe
/iconv.dll
) approach with systems written in other languages, e.g. C#
, with equally painless results.
P.S. It works with Python 2.4 as well as Python 2.5 and later. Not tested with other versions, though I don't foresee any problems.
Solution 2
After a LOT of digging, I found a package that worked for me. Although it is said to support the generation of keys, I didn't test it. However I did manage to decrypt a message that was encrypted using a GPG public key. The advantage of this package is that it does not require a GPG executable file on the machine, and is a Python based implementation of the OpenPGP (rather than a wrapper around the executable). I created the private and public keys using GPG4win and kleopatra for windows See my code below.
import pgpy
emsg = pgpy.PGPMessage.from_file(<path to the file from the client that was encrypted using your public key>)
key,_ = pgpy.PGPKey.from_file(<path to your private key>)
with key.unlock(<your private key passpharase>):
print (key.decrypt(emsg).message)
Although the question is very old. I hope this helps future users.
Solution 3
PyCrypto supports PGP - albeit you should test it to make sure that it works to your specifications.
Although documentation is hard to come by, if you look through Util/test.py (the module test script), you can find a rudimentary example of their PGP support:
if verbose: print ' PGP mode:',
obj1=ciph.new(password, ciph.MODE_PGP, IV)
obj2=ciph.new(password, ciph.MODE_PGP, IV)
start=time.time()
ciphertext=obj1.encrypt(str)
plaintext=obj2.decrypt(ciphertext)
end=time.time()
if (plaintext!=str):
die('Error in resulting plaintext from PGP mode')
print_timing(256, end-start, verbose)
del obj1, obj2
Futhermore, PublicKey/pubkey.py provides for the following relevant methods:
def encrypt(self, plaintext, K)
def decrypt(self, ciphertext):
def sign(self, M, K):
def verify (self, M, signature):
def can_sign (self):
"""can_sign() : bool
Return a Boolean value recording whether this algorithm can
generate signatures. (This does not imply that this
particular key object has the private information required to
to generate a signature.)
"""
return 1
Solution 4
As other have noted, PyMe is the canonical solution for this, since it's based on GpgME, which is part of the GnuPG ecosystem.
For Windows, I strongly recommend to use Gpg4win as the GnuPG distribution, for two reasons:
It's based on GnuPG 2, which, among other things, includes gpg2.exe
, which can (finally, I might add :) start gpg-agent.exe
on-demand (gpg v1.x can't).
And secondly, it's the only official Windows build by the GnuPG developers. E.g. it's entirely cross-compiled from Linux to Windows, so not a iota of non-free software was used in preparing it (quite important for a security suite :).
Solution 5
PyMe does claim full compatibility with Python 2.4, and I quote:
The latest version of PyMe (as of this writing) is v0.8.0. Its binary distribution for Debian was compiled with SWIG v1.3.33 and GCC v4.2.3 for GPGME v1.1.6 and Python v2.3.5, v2.4.4, and v2.5.2 (provided in 'unstable' distribution at the time). Its binary distribution for Windows was compiled with SWIG v1.3.29 and MinGW v4.1 for GPGME v1.1.6 and Python v2.5.2 (although the same binary get installed and works fine in v2.4.2 as well).
I'm not sure why you say "it doesn't seem to be compatible with Python 2.4 which I have to use" -- specifics please?
And yes it does exist as a semi-Pythonic (SWIGd) wrapper on GPGME -- that's a popular way to develop Python extensions once you have a C library that basically does the job.
PyPgp has a much simpler approach -- that's why it's a single, simple Python script: basically it does nothing more than "shell out" to command-line PGP commands. For example, decryption is just:
def decrypt(data):
"Decrypt a string - if you have the right key."
pw,pr = os.popen2('pgpv -f')
pw.write(data)
pw.close()
ptext = pr.read()
return ptext
i.e., write the encrypted cyphertext to the standard input of pgpv -f
, read pgpv's standard output as the decrypted plaintext.
PyPgp is also a very old project, though its simplicity means that making it work with modern Python (e.g., subprocess instead of now-deprecated os.popen2) would not be hard. But you still do need PGP installed, or PyPgp won't do anything;-).
Greg
I'm an avid programmer, web developer and electronics enthusiast. Here's my gift to Python hackers. And you can see everything I'm up to here.
Updated on July 20, 2020Comments
-
Greg almost 4 years
I'm making a program in Python to be distributed to windows users via an installer.
The program needs to be able to download a file every day encrypted with the user's public key and then decrypt it.
So I need to find a Python library that will let me generate public and private PGP keys, and also decrypt files encrypted with the public key.
Is this something pyCrypto will do (documentation is nebulous)? Are there other pure Python libraries? How about a standalone command line tool in any language?
All I saw so far was GNUPG but installing that on Windows does stuff to the registry and throws dll's everywhere, and then I have to worry about whether the user already has this installed, how to backup their existing keyrings, etc. I'd rather just have a python library or command line tool and mange the keys myself.
Update: pyME might work but it doesn't seem to be compatible with Python 2.4 which I have to use.
-
Greg almost 15 yearsPyMe was this issue: stackoverflow.com/questions/1030297/…
-
Greg almost 15 yearsI don't see a way to generate keys, or accept another party's public key to encrypt data with. Any ideas?
-
Alex Martelli almost 15 yearsYeah, theit claim that I quoted (about Windows, specifically) is ill-founded (in Windows, only, extensions need to be compiled for specific versions of Python, so one compiled for 2.5 will not work for 2.4). All you need is to recompile (which requires MSVC 6.0).
-
Greg almost 15 yearsThanks that sounds perfect. I had no idea I could just pull the exe out of gpg. Where will it put the keyring? Will this method cause any problems if the user already has gpg installed? Any chance I could just see how your code does key generation, and key imports?
-
Greg almost 15 yearsAlso be sure to update us here when you release your final code.
-
Vinay Sajip almost 15 yearsWhen you call gpg, specify the path where you want the keyrings to be stashed using the --homedir argument. (This is already done for you using Traugott's module - there's a GPG class whose constructor takes a gnupghome parameter which you can set to this directory.) The pubring.gpg, secring.gpg and trustdb.gpg are created in the folder specified by --homedir. I don't believe this will cause a problem if the user has gpg already installed - the --homedir argument should override any values in the registry.
-
Vinay Sajip almost 15 yearsJust make sure that the gpg which gets executed is your bundled copy - another installed copy (which might be on the path) may be a different version which would be wise to avoid just as a precautionary measure to head off any interoperability issues.
-
Vinay Sajip almost 15 yearsSee paste.pocoo.org/show/125608 for an excerpts from the unit tests for the module. This shows key generation and key importing. Thanks for accepting the answer. I'll be sure to post a comment here when I release the code.
-
dlitz about 12 yearsNo, MODE_PGP in PyCrypto is legacy, experimental code that probably never worked right. (See bugs.launchpad.net/pycrypto/+bug/996814). Don't use it. We'll be removing it from PyCrypto soon anyway.
-
raylu almost 11 yearsThen what do you suggest, @dlitz?
-
wim about 7 years@VinaySajip Hi there Vinay, thanks for your work on
gnupg
. Could you comment on the relationship between this module and the python-gnupg project on github? Considering this answer is about 7 years old now - what's current, and which should a new user prefer? (both projects seem well maintained and well documented, so the choice is not obvious) -
Vinay Sajip about 7 years@wim The other module is a fork (without any discussion with me) by another developer, and not a drop-in replacement. Obviously, I would recommend my project as "the original and best"! The other project is GPLv3, my project is BSD. The other project cites a
shell=True
vulnerability which has not been in my project for a long time. To decide which to use, pick some features you need, and see which project has those/makes them easy to use. -
lazyList over 6 yearsThank you for the info. Can this actually perform encryption using the public key ? Also, a link to the package maybe helpful.
-
lazyList over 6 yearsThank you @Roee Anuar. Already used it to build a PGP based password protection program. Appreciate the help.
-
itsafire almost 6 yearsThe provided link within the answer is stale.
-
Prabhash Jha over 4 yearsIs there an equivalent example where I can encrypt file using public key (pgpy) ?
-
Antonio Bardazzi almost 4 yearsAdded an answer with code sample for pgpy and gpg to use just the public key exported file.
-
Kar almost 4 yearsUsing PGP, Whats the Python function to generate public /private key -->
rkey_fpath
-
Kar almost 4 yearsI tried your logic but got an error
TypeError: 'odict_values' object is not subscriptable
. I have exported mypublic key tomy-pubkey.asc
-
Steven Magana-Zook about 3 years@lazyList here is the documentation that includes capabilities and installation instructions. pgpy.readthedocs.io/en/latest/index.html