PGPy - Pretty Good Privacy for Python
PGPy is a Python library that implements Pretty Good Privacy (PGP) as described in RFC 4880. It provides capabilities for key generation, encryption, decryption, and signature management. The library is currently at version 0.6.0 and has an irregular release cadence, with major changes often accompanied by Python version requirement updates.
Common errors
-
AttributeError: module 'cryptography.utils' has no attribute 'register_interface'
cause This error occurs due to an incompatibility between `pgpy` and newer versions of its `cryptography` dependency, where a breaking change in `cryptography` removed the `register_interface` attribute.fixDowngrade the `cryptography` library to a compatible version (e.g., `cryptography==37.0.4`) and then reinstall `pgpy` if necessary. You can do this with `pip install cryptography==37.0.4`. -
ImportError: No module named pgpy
cause The `pgpy` package is either not installed in the active Python environment or the script is being executed with a different Python interpreter where `pgpy` is not present.fixEnsure `pgpy` is installed for the correct Python interpreter by running `pip install pgpy`. If using a virtual environment, make sure it is activated. -
ValueError: Expected: ASCII-armored PGP data
cause This error typically arises when attempting to load PGP data (key or message) using `PGPKey.from_blob()` or `PGPKey.from_file()` methods, but the input data is in a binary format or is malformed, and `pgpy` expects ASCII-armored (text-based) PGP data.fixEnsure the input string or file content is a valid ASCII-armored PGP block, including the `-----BEGIN PGP ... BLOCK-----` headers and footers. If you are intentionally trying to load binary PGP data, `pgpy.PGPKey.from_file()` and `pgpy.PGPMessage.from_file()` can often handle both, but `from_blob` might be more particular, and the input itself must be correctly formatted binary PGP data. -
pgpy.errors.PGPError: Key ... does not have the required usage flag EncryptStorage, EncryptCommunications
cause The PGP public key being used for encryption does not have the necessary 'Encrypt Communications' or 'Encrypt Storage' usage flags (KeyFlags) explicitly set, which is a requirement of the OpenPGP standard for encryption operations.fixGenerate a new PGP key or subkey with the appropriate `KeyFlags.EncryptCommunications` and/or `KeyFlags.EncryptStorage` set. When creating keys, ensure you specify these flags for encryption purposes.
Warnings
- breaking Python 2.7 and 3.4 support was officially dropped in PGPy v0.6.0. Attempts to install or run PGPy v0.6.0+ on these Python versions will fail.
- gotcha Passphrase encoding for keys changed from Latin-1 to UTF-8 in v0.5.3. This may cause compatibility issues when importing keys generated with older PGPy versions or interacting with other PGP software that expects Latin-1.
- gotcha Versions of PGPy prior to v0.5.4 had compatibility breaks with Python 3.8 and newer, specifically related to importing ABCs from `collections`.
- breaking Version 0.3.0 introduced 'semi-significant API changes' that could break existing code. This included major updates to signature generation/verification and encryption/decryption APIs.
Install
-
pip install pgpy
Imports
- PGPKey
from pgpy.pgp import PGPKey
- PGPMessage
from pgpy.pgp import PGPMessage
- PGPUID
from pgpy.pgp import PGPUID
- constants
from pgpy import constants
Quickstart
import pgpy
from pgpy.constants import PubKeyAlgorithm, KeyFlags, HashAlgorithm, SymmetricKeyAlgorithm, CompressionAlgorithm
# 1. Generate a new RSA PGP key
key = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 4096)
# 2. Create a User ID
uid = pgpy.PGPUID.new('Test User', comment='example', email='test@example.com')
# 3. Add the User ID to the key, defining its capabilities
key.add_uid(uid,
usage={KeyFlags.Sign, KeyFlags.Encrypt},
hashes=[HashAlgorithm.SHA512, HashAlgorithm.SHA256],
ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128],
compression=[CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed])
# 4. (Optional) Protect the private key with a passphrase
passphrase = "my_secret_passphrase"
key.protect(passphrase, SymmetricKeyAlgorithm.AES256, HashAlgorithm.SHA256)
# 5. Create a PGP message
message_to_encrypt = pgpy.PGPMessage.new("This is a secret message that needs to be encrypted.")
# 6. Encrypt the message using the public key part of the generated key
encrypted_message = key.encrypt(message_to_encrypt)
# 7. Decrypt the message using the private key part
# If the key is protected, it must be unlocked first.
if key.is_protected:
with key.unlock(passphrase):
decrypted_message = key.decrypt(encrypted_message)
else:
decrypted_message = key.decrypt(encrypted_message)
print(f"Original message: {message_to_encrypt.message}")
print(f"Decrypted message: {decrypted_message.message}")
# You can also export the key to an armored string
# public_key_armor = str(key.pubkey)
# private_key_armor = str(key) # WARNING: Handle private keys with extreme care!