Here's a full script that will:
- Attempt to decrypt all the files in a given folder that were encrypted with your public key.
- Write the new files to a specified folder.
- Move the encrypted files to a specified folder.
The script also has everything you need to create and store your own private and public keys, check out the "First time set up" section below.
The idea is that you can schedule this script to run as often as you like, and it'll automatically decrypt data found and store it for you.
I hope this helps someone, this was a tricky project to figure out.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ Introduction, change log and table of contents
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Purpose: This script is used to encrypt and decrypt files using the PGP (Pretty Good Privacy) standard..
#
# Change date Changed by Description
# 2022-10-03 Ryan Bradley Initial draft
# 2022-10-12 Ryan Bradley Cleaned up some comments and table of contents.
#
# Table of Contents
# [1.0] Hard-coded variables
# [1.1] Load packages and custom functions
# [1.3] First time set up
# [1.4] Define custom functions
# [2.0] Load keys and decrypt files
#
# Sources used to create this script, and for further reading:
# https://github.com/SecurityInnovation/PGPy/
# https://mcmap.net/q/370399/-how-to-do-pgp-in-python-generate-keys-encrypt-decrypt
# https://pypi.org/project/PGPy/
# https://betterprogramming.pub/creating-a-pgp-encryption-tool-with-python-19bae51b7fd
# https://pgpy.readthedocs.io/en/latest/examples.html
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [1.1] Load packages
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import glob
import pgpy
import shutil
import io
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [1.2] Hard-coded variables
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define the paths to public and private keys
path_public_key = r'YOUR PATH HERE'
path_private_key = r'YOUR PATH HERE'
# Define paths to files you want to try decrypting
path_original_files = r'YOUR PATH HERE'
path_decrypted_files = r'YOUR PATH HERE'
path_encrypted_files= r'YOUR PATH HERE'
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [1.3] First time set up
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# IMPORTANT WARNINGS!!!!
# - Do NOT share your private key with anyone else.
# - You MUST have the associated private key that is is generated along with a public key
# if you want to be able to decrypt anything that is encryped with that public key. Do
# not overwrite the existing keys unless you will never need any of the previously
# encryped data.
# - Do not generate new public and private keys unless you have a good reason to.
#
# The following steps will walk you through how to create and write public and private keys to
# a network location. Be very careful where you store this information. Anyone with access
# to your private key can decrypt anything that was encryped with your public key.
#
# These steps only need to be performed one time when the script is first being
# created. They are commented out intentionally, as they shouldn't need to be performed
# every time the script is ran.
#
# Here's the a link to the documentation on this topic:
# https://pgpy.readthedocs.io/en/latest/examples.html
# # Load the extra things we need to define a new key
# from pgpy.constants import PubKeyAlgorithm, KeyFlags, HashAlgorithm, SymmetricKeyAlgorithm, CompressionAlgorithm
# # Gerate a new a primary key. For this example, we'll use RSA, but it could be DSA or ECDSA as well
# key = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 4096)
# # Define a new user
# uid = pgpy.PGPUID.new('USER_NAME', comment='Generic user', email='YOUR_EMAIL')
# # Add the new user id to the key, and define all the key preferences.
# key.add_uid(uid, usage={KeyFlags.Sign, KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage},
# hashes=[HashAlgorithm.SHA256, HashAlgorithm.SHA384, HashAlgorithm.SHA512, HashAlgorithm.SHA224],
# ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128],
# compression=[CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed]
# , is_compressed = True)
# # Write the ASCII armored public key to a network location.
# text_file = open(path_public_key, 'w')
# text_file.write(str(key.pubkey))
# text_file.close()
# # Write the ASCII armored private key to a network location.
# text_file = open(path_private_key, 'w')
# text_file.write(str(key))
# text_file.close()
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [1.4] Define custom functions
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def file_encrypt(path_original_file, path_encrypted_file, key_public):
"""
A function that encrypts the content of a file at the given path and
creates an ecryped version file at the new location using the specified
public key.
"""
# Create a PGP file, compressed with ZIP DEFLATE by default unless otherwise specified
pgp_file = pgpy.PGPMessage.new(path_original_file, file=True)
# Encrypt the data with the public key
encrypted_data = key_public.encrypt(pgp_file)
# Write the encryped data to the encrypted destination
text_file = open(path_encrypted_file, 'w')
text_file.write(str(encrypted_data))
text_file.close()
def file_decrypt(path_encrypted_file, path_decrypted_file, key_private):
"""
A function that decrypts the content of a file at path path and
creates a decrypted file at the new location using the given
private key.
"""
# Load a previously encryped message from a file
pgp_file = pgpy.PGPMessage.from_file(path_encrypted_file)
# Decrypt the data with the given private key
decrypted_data = key_private.decrypt(pgp_file).message
# Read in the bytes of the decrypted data
toread = io.BytesIO()
toread.write(bytes(decrypted_data))
toread.seek(0) # reset the pointer
# Write the data to the location
with open(path_decrypted_file, 'wb') as f:
shutil.copyfileobj(toread, f)
f.close()
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [2.0] Load keys and decrypt files
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Load your pre-generated public key from the network
key_public, _ = pgpy.PGPKey.from_file(path_public_key)
# Load your pre-generated public key from the network
key_private, _ = pgpy.PGPKey.from_file(path_private_key)
# Find and process any encrypted files in the landing folder
for file in glob.glob(path_original_files + '\*.pgp'):
# Get the path to the file we need to decrypt
path_encrypted_file = str(file)
# Extract the file name
parts = path_encrypted_file.split('\\')
str_file_name = parts[len(parts)-1]
str_clean_file_name = str_file_name[:-4]
# Extract the file exension
str_extension = str_clean_file_name.split('.')
str_extension = str_extension[len(str_extension) - 1]
# Create the path to the new decryped file, dropping the ".pgp" extension
path_decrypted_file = path_decrypted_files + '\\' + str_clean_file_name
# Create the path to the place we'll store the encryped file
path_archived_encrypted_file = path_encrypted_files + '\\' + str_file_name
# Decrypt the file
try:
file_decrypt(path_encrypted_file, path_decrypted_file, key_private)
# Move the encryped file to its new location
shutil.move(path_encrypted_file, path_archived_encrypted_file)
except:
print('DECRYPTION ERROR!')
print(f'Unable to decrypt {path_encrypted_file}')
# Just for reference, here's how you would call the function to encrypt a file:
# file_encrypt(path_original_file, path_encrypted_file, key_public)