← Back to list

Understanding PGP (Pretty Good Privacy)

Unlock the secrets of PGP (Pretty Good Privacy) — from key generation and encryption to signing and verification.

Aditya Ramaswamy · 2025-12-02 11:23 · 6 claps · 15.1 min read
#pgp #pgpencryption #securefiletransfer #python #digital-signatures
Open on Medium ↗
Wiki topics: 🔒 · Cybersecurity 🏔️ · Outdoor & Adventure

Understanding PGP (Pretty Good Privacy)

1. Introduction to PGP

Pretty Good Privacy, or PGP, is one of the most influential and enduring technologies in the world of digital security. Created in 1991 by Phil Zimmermann, PGP emerged as a response to growing concerns about privacy in electronic communication. What started as a personal project quickly evolved into a global standard for secure messaging, email encryption, and file protection.

At its core, PGP solves a fundamental problem: How do two people who have never met share information securely over an insecure channel like the internet?

PGP’s answer is a hybrid cryptographic model — a smart combination of symmetric and asymmetric encryption. This design allows PGP to offer the best of both worlds: the speed of symmetric algorithms and the security of public-key cryptography. Over time, it also introduced mechanisms for authentication, digital signatures, and integrity checks, making it a comprehensive security framework rather than just an encryption tool.

PGP became widely adopted because it addressed three critical security needs in a single flow:

  • Confidentiality — ensuring only the intended recipient can read the message
  • Integrity — detecting any tampering in transit
  • Authentication — verifying who actually sent the message

While PGP was initially popular among privacy advocates and technologists, it has since influenced numerous standards and modern tools, including OpenPGP, GnuPG (GPG), secure email systems, password managers, and even blockchain-based identity systems.

Today, PGP stands not only as a reliable tool but also as a foundational concept in how we think about secure communication on the internet.

2. How PGP Works

To understand how PGP secures communication, it helps to break it down into the core ideas that power it. PGP doesn’t rely on just one cryptographic technique — instead, it uses a hybrid encryption model that blends multiple layers of protection into a single workflow. Each layer plays a specific role: encryption, authentication, and integrity checking.

Let’s walk through the major components and the overall flow.

2.1 Hybrid Cryptography (The Heart of PGP)

PGP combines two types of cryptography:

A. Symmetric Encryption (Fast)

  • Used to encrypt the actual message or file.
  • Relies on a session key (a one-time random key).
  • Algorithms commonly used: AES, IDEA (historically), Triple-DES.

B. Asymmetric Encryption (Secure Key Exchange)

  • Used to encrypt the session key.
  • Utilizes a public-private keypair.
  • Algorithms commonly used: RSA, DSA, ElGamal.

This combination is powerful: ✔ The message encrypts quickly ✔ The session key is protected securely ✔ Only the intended recipient can decrypt it

2.2 Key Elements Inside PGP

Public/Private Keypair

Every user has:

  • A public key (shared widely)
  • A private key (kept secret)

The public key encrypts the session key while the private key decrypts it.

The Web of Trust

Unlike certificate authorities, PGP uses a decentralized trust model where:

  • Users sign each other’s public keys
  • Trust is built through relationships

It allows individuals to verify identity without needing a central authority.

Key Servers

Public repositories where users can upload and discover public keys.

2.3 The PGP Workflow

Here’s the high-level lifecycle of how PGP is used:

Step 1: Generating Keys

Each user generates:

  • One or more public/private keypairs
  • Optional subkeys for specific purposes (signing, encryption, authentication)

Step 2: Encrypting a Message

When Alice wants to send a secure message to Bob:

  1. Alice creates a random session key
  2. She encrypts the message using the session key (symmetric encryption)
  3. She encrypts the session key with Bob’s public key (asymmetric encryption)

This produces a PGP-encrypted package.

Step 3: Adding a Digital Signature

To ensure authenticity:

  1. Alice hashes the message
  2. She encrypts the hash with her private key
  3. This signature allows Bob to verify the sender and integrity

Step 4: Sending the Message

The encrypted message + encrypted session key + signature is sent over email or any medium.

Step 5: Decryption by the Recipient

When Bob receives the message:

  1. He uses his private key to decrypt the session key
  2. He then decrypts the message using the session key
  3. He verifies Alice’s signature using her public key

Bob can now be sure that:

  • The message is unreadable to anyone else
  • The message is intact
  • Alice is the real sender

2.4 Integrity & Checksum

PGP automatically includes:

  • Message digests (SHA-256, SHA-1 historically)
  • Built-in integrity protections (Modification Detection Code)

This ensures that even slight tampering is detectable.

2.5 Putting It All Together

PGP works because it tightly weaves together:

  • Symmetric encryption (for speed)
  • Asymmetric encryption (for secure key exchange)
  • Digital signatures (for authenticity)
  • Hashing (for integrity)
  • Decentralized trust (Web of Trust)

The result is a robust and flexible architecture for protecting data in motion and at rest.

3. Real-World Example: Secure File Transfer Using PGP

While PGP is popularly associated with secure email, one of its most common real-world applications — especially in enterprises and banking — is secure file transfer. Whether it’s sending transaction files, audit logs, customer statements, or regulatory reports, organizations often rely on PGP to ensure that sensitive data travels safely across networks and systems.

Let’s walk through a practical example of how PGP is used to securely transfer a file from one system (Alice) to another (Bob).

3.1 Scenario Setup

  • Alice works in an organization that needs to send a confidential financial report to a partner institution.
  • Bob, at the receiving institution, must ensure the file is authentic and untampered before processing it.
  • Both parties have exchanged PGP public keys beforehand.

The file in question: **Daily_Transactions_Report.csv**

3.2 Step-by-Step Flow

Step 1: Bob Shares His Public Key

Before any transfer happens, Bob publishes or securely shares his PGP public key with Alice. This allows Alice to encrypt the file in a way that only Bob can decrypt.

Step 2: Alice Prepares the File

Alice finalizes the file Daily_Transactions_Report.csv. Before sending it, she must ensure:

  • Only Bob can read it (confidentiality)
  • Bob can verify it hasn’t been altered (integrity)
  • Bob can be sure Alice was the sender (authenticity)

PGP handles all three in one workflow.

Step 3: Alice Encrypts the File

Alice uses Bob’s public key to encrypt the file.

Behind the scenes:

  1. PGP generates a one-time session key
  2. The file is encrypted using the session key (symmetric encryption)
  3. The session key is encrypted using Bob’s public key (asymmetric encryption)

Alice now has a secure PGP file, typically saved as:

**Daily_Transactions_Report.csv.pgp or `Daily_Transactions_Report.csv.gpg`**

Step 4: Alice Signs the File

To prove authenticity, Alice signs the encrypted file with her private key.

This allows Bob to check:

  • Who sent the file
  • If the file was modified at any point

A digital signature is attached as part of the resulting PGP package.

Step 5: Secure Transfer

Alice sends the signed and encrypted .pgp file using any transfer method:

  • SFTP
  • Email
  • API upload
  • Cloud storage link
  • Automated file exchange system (MFT)

Even if the transfer channel is insecure, the data remains protected.

Step 6: Bob Receives and Decrypts the File

Bob receives the .pgp file.

He:

  1. Uses his private key to decrypt the session key
  2. Uses the session key to decrypt the file back to plaintext
  3. Verifies Alice’s digital signature using her public key

If all validations succeed, Bob confidently retrieves:

**Daily_Transactions_Report.csv**

He now knows:

✔ The file is from Alice ✔ The file wasn’t tampered with ✔ No unauthorized party could have read it during transit

3.3 Why This Matters in the Real World

This workflow is widely used in:

  • Banking & fintech file exchanges
  • Government data sharing
  • Healthcare record transfers
  • Enterprise MFT platforms
  • Automated batch processing systems
  • Internal secure backups

It solves a major operational challenge: How do two organizations securely exchange files without needing a secure channel?

PGP answers this by embedding the security directly into the file itself — not the transport mechanism.

4. Python Example: Encrypting, Signing, Transferring & Decrypting a File Using PGP

While GPG CLI is common in enterprises, many automated systems (banking batch jobs, scheduled transfers, MFT platforms, backend pipelines) require programmatic PGP operations. Python provides an easy way to integrate PGP into applications using the python-gnupg library.

A high-level flow is shown in the following image.

PGP Workflow

PGP Workflow

Overall Module Design

There are 3 main classes:

  • PGPKeyManager : Generates and Loads RSA Key Pairs
  • PGPEncryptor : Encrypts Data using Public Key + AES-256 and also Generates Signature using Private Key
  • PGPDecryptor : Decrypts Data using Private Key + Verifies Signature using Public Key

4.1 Install the required library

The main GnuPG (gpg) needs to be installed on the system, since the Python library just acts as a wrapper around it. In Linux this is probably installed and for Windows, you can find here.

pip install python-gnupg

4.2 PGPKeyManager Class

This class takes care of generating the RSA Key Pairs , exporting them as Public and Private Keys and also importing the keys. The main part is to ensure that the GnuPG is setup correctly , since the python wrapper internally calls the gpg library passing the required parameters.

class PGPKeyManager:

    def __init__(self, gpg_home="pgp_keys", logger=None):
        self.gpg_home = gpg_home
        self.logger = logger
        os.makedirs(gpg_home, exist_ok=True)
        # Setup the GnuPG(gpg) tool as required 
        self.gpg = gnupg.GPG(gnupghome=self.gpg_home,
                             use_agent=False,
                             options=["--pinentry-mode", "loopback"],
                             # If you binary is located in some other location
                             # gpgbinary="C:\\Program Files (x86)\\GnuPG\\bin\\gpg.exe",
                             verbose=False
                             )

        self.logger.debug(f" Public Keys : {self.gpg.list_keys()}")
        self.logger.debug(f" Private Keys : {self.gpg.list_keys(True)}")

    def generate_keys(self, name, email, passphrase):

        self.logger.info("Generating keys...")
        input_data = self.gpg.gen_key_input(
            name_real=name,
            name_email=email,
            key_type="RSA",
            key_length=2048,
            passphrase=passphrase,
        )

        key = self.gpg.gen_key(input_data)
        self.logger.info(f"Keys generated: {key}")
        return key.fingerprint

    def export_public_key(self, fingerprint, output_path):
        self.logger.info(f"Exporting public key : {output_path}")
        with open(output_path, "w") as f:
            f.write(
                self.gpg.export_keys(fingerprint)
            )

    def export_private_key(self, fingerprint, output_path, passphrase):
        self.logger.info(f"Exporting private key : {output_path}")
        with open(output_path, "w") as f:
            f.write(
                self.gpg.export_keys(fingerprint, passphrase=passphrase, secret=True)
            )

    def import_key(self, file_path):
        self.logger.info(f"Importing key : {file_path}")
        with open(file_path, "r") as f:
            return self.gpg.import_keys(f.read())

4.3 PGPEncryptor Class

This class takes care of encrypting a given file using PGP. Along with this , it will also take care of signing.

class PGPEncryptor:

    def __init__(self, gpg,receiver_public_key, sender_private_key, sender_passphrase, logger):
        self.gpg = gpg
        self.logger = logger
        self.sender_passphrase = sender_passphrase

        with open(receiver_public_key, "rb") as key_file:
            receiver_key_data = key_file.read()

        with open(sender_private_key, "rb") as key_file:
            sender_key_data = key_file.read()

        self.receiver_key = self._import_key(receiver_key_data)
        self.sender_key = self._import_key(sender_key_data)
        self.logger.debug(f"Receiver public key: {self.receiver_key}")
        self.logger.debug(f"Sender public key: {self.sender_key}")

    def _import_key(self, key_data):
        self.logger.info(f"Importing key...")
        imported = self.gpg.import_keys(key_data)
        self.logger.info(f"Imported : {imported.__dict__}")
        if not imported.fingerprints:
            self.logger.error(f"key import failed!")
            raise Exception(f"key import failed!")

        self.logger.info(f"key imported successfully")
        return imported.fingerprints[0]

    def encrypt_file(self, input_file_path, output_file_path):

        self.logger.info(f"Starting encryption for file: {input_file_path}")

        with open(input_file_path, "rb") as input_file:

            status = self.gpg.encrypt_file(
                input_file,
                recipients=[self.receiver_key],
                sign=True,
                passphrase=self.sender_passphrase,
                always_trust=True,
                output=output_file_path,
                # extra_args=["--pinentry-mode", "loopback"]
                extra_args = ["--pinentry-mode", "loopback", "--local-user", self.sender_key]
            )

        is_sig_created = False
        if status.ok:
            self.logger.info(f"Encryption Successful : {status.ok}")
            # Custom code to check if signature is generated or not 
            # Since the current library does not parse the status correctly
            for line in status.stderr.splitlines():
                if "SIG_CREATED" in line:
                    self.logger.info(f"SIG_CREATED: {line}")
                    is_sig_created = True
                    break

            if not is_sig_created:
                self.logger.error(f"Signature not created")

        else:
            self.logger.error(f"Encryption Failure : {status.stderr}")
        return {
            "ok": status.ok,
            "status": status.status,
            "stderr": status.stderr,
        }

4.4 PGPDecryptor Class

This class takes care of decrypting the file. It also takes care of verifying the signature.

class PGPDecryptor:

    def __init__(self, gpg,receiver_private_key, sender_public_key, receiver_passphrase,logger):
        self.gpg = gpg
        self.logger = logger
        self.receiver_private_key = receiver_private_key
        self.sender_public_key = sender_public_key
        self.receiver_passphrase = receiver_passphrase

        # Import the Keys
        self.receiver_fingerprint = self._import_key(receiver_private_key)
        self.sender_fingerprint = self._import_key(sender_public_key)

    def _import_key(self, key_path):

        self.logger.info(f"Importing key from {key_path}")

        with open(key_path, "rb") as key_file:
            key_data = key_file.read()

        imported = self.gpg.import_keys(key_data)
        if not imported.fingerprints:
            self.logger.error("Key import failed!")
            raise Exception("Key import failed")

        self.logger.info("Key imported successfully")
        return imported.fingerprints[0]

    def decrypt_file(self, input_file_path,output_file_path):

        self.logger.info(f"Starting decryption for file: {input_file_path}")

        with open(input_file_path, "rb") as input_file:
            result = self.gpg.decrypt_file(
                input_file,
                passphrase=self.receiver_passphrase,
                output=output_file_path,
            )

        if not result.ok:
            self.logger.error(f"Failed to decrypt: {input_file_path}, Error : {result.stderr}")
            raise Exception("Failed to decrypt")
        else:
            self.logger.info(f"Decryption Successful {result.ok}")
            self.logger.info(f"Signature Valid : {result.valid}")
            self.logger.info(f"Signed By: {result.username}")

        self.logger.info(f"Finished decryption for file: {input_file_path}")

4.5 Code Execution

Below is the main.py file that performs the operations of key generation , encryption and decryption.

if __name__ == "__main__":

    LOGGER = setup_logging()

    operation = input("Enter the operation to perform: 1 for key generation, 2 for encryption, 3 for decryption: ")
    manager = PGPKeyManager(gpg_home="resources/pgp_keys2",logger=LOGGER)
    operation = int(operation)
    if operation == 1:

        # Generate Key Pairs
        # For Sender
        sender_fp = manager.generate_keys("Sender", "sender@bank.com", "sender123")

        # For Receiver
        receiver_fp = manager.generate_keys("Receiver", "receiver@bank.com", "receiver123")

        # Export the Public and Private Keys
        # For Sender
        manager.export_public_key(sender_fp, output_path="resources/sender_public.asc")
        manager.export_private_key(sender_fp, output_path="resources/sender_private.asc",passphrase="sender123")

        # For Receiver
        manager.export_public_key(receiver_fp, output_path="resources/receiver_public.asc")
        manager.export_private_key(receiver_fp, output_path="resources/receiver_private.asc", passphrase="receiver123")

    elif operation == 2:
        # Let try encrypting and decrypting a sample file
        # Sample File
        with open("bank_data.txt", "w") as f:
            f.write("Sample file for PGP Encryption")

        # Encrypt the File
        encryptor = PGPEncryptor(manager.gpg,
                                 receiver_public_key="resources/receiver_public.asc",
                                 sender_private_key="resources/sender_private.asc",
                                 sender_passphrase="sender123",
                                 logger=LOGGER
                                 )
        result = encryptor.encrypt_file(
            input_file_path="bank_data.txt",
            output_file_path="bank_data.txt.pgp"
        )

    elif operation == 3:
        # Lets Decrypt and Verify Signature of the Encrypted File
        decryptor = PGPDecryptor(manager.gpg,
                                 receiver_private_key="resources/receiver_private.asc",
                                 sender_public_key="resources/sender_public.asc",
                                 receiver_passphrase="receiver123",
                                 logger=LOGGER
                                 )

        # Perform the Operation
        decryptor.decrypt_file(
            input_file_path="bank_data.txt.pgp",
            output_file_path="decrypted_bank_data.txt.",
        )

Sample Execution Trace

(venv3) root@aa8e6a4afaaa:/home/practical_cryptography_series/pgp_approach/pgp_flow# python main.py
Enter the operation to perform: 1 for key generation, 2 for encryption, 3 for decryption: 1
2025-12-02 11:46:07 | INFO | PGP-AUDIT | Generating keys...
2025-12-02 11:46:10 | INFO | PGP-AUDIT | Keys generated: BE5760FE90B4E31D286E848227329C37E9ADCB4B
2025-12-02 11:46:10 | INFO | PGP-AUDIT | Generating keys...
2025-12-02 11:46:11 | INFO | PGP-AUDIT | Keys generated: C312B16B4DCA642EF3C2D268D7C1DC47356C7772
2025-12-02 11:46:11 | INFO | PGP-AUDIT | Exporting public key : resources/sender_public.asc
2025-12-02 11:46:11 | INFO | PGP-AUDIT | Exporting private key : resources/sender_private.asc
2025-12-02 11:46:12 | INFO | PGP-AUDIT | Exporting public key : resources/receiver_public.asc
2025-12-02 11:46:12 | INFO | PGP-AUDIT | Exporting private key : resources/receiver_private.asc

(venv3) root@aa8e6a4afaaa:/home/practical_cryptography_series/pgp_approach/pgp_flow# python main.py
Enter the operation to perform: 1 for key generation, 2 for encryption, 3 for decryption: 2
2025-12-02 11:46:55 | INFO | PGP-AUDIT | Importing key...
2025-12-02 11:46:55 | INFO | PGP-AUDIT | Imported : {'gpg': <gnupg.GPG object at 0x7ab00d8f8710>, 'results': [{'fingerprint': 'C312B16B4DCA642EF3C2D268D7C1DC47356C7772', 'ok': '0', 'text': 'Not actually changed\n'}], 'fingerprints': ['C312B16B4DCA642EF3C2D268D7C1DC47356C7772'], 'count': 1, 'no_user_id': 0, 'imported': 0, 'imported_rsa': 0, 'unchanged': 1, 'n_uids': 0, 'n_subk': 0, 'n_sigs': 0, 'n_revoc': 0, 'sec_read': 0, 'sec_imported': 0, 'sec_dups': 0, 'not_imported': 0, 'data': b'', 'stderr': 'gpg: WARNING: unsafe permissions on homedir \'/home/practical_cryptography_series/pgp_approach/pgp_flow/resources/pgp_keys3\'\n[GNUPG:] IMPORT_OK 0 C312B16B4DCA642EF3C2D268D7C1DC47356C7772\n[GNUPG:] KEY_CONSIDERED C312B16B4DCA642EF3C2D268D7C1DC47356C7772 0\ngpg: key D7C1DC47356C7772: "Receiver <receiver@bank.com>" not changed\ngpg: Total number processed: 1\ngpg:              unchanged: 1\n[GNUPG:] IMPORT_RES 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0\n', 'returncode': 0}
2025-12-02 11:46:55 | INFO | PGP-AUDIT | key imported successfully
2025-12-02 11:46:55 | INFO | PGP-AUDIT | Importing key...
2025-12-02 11:46:55 | INFO | PGP-AUDIT | Imported : {'gpg': <gnupg.GPG object at 0x7ab00d8f8710>, 'results': [{'fingerprint': 'BE5760FE90B4E31D286E848227329C37E9ADCB4B', 'ok': '0', 'text': 'Not actually changed\n'}, {'fingerprint': 'BE5760FE90B4E31D286E848227329C37E9ADCB4B', 'ok': '16', 'text': 'Not actually changed\nContains private key\n'}], 'fingerprints': ['BE5760FE90B4E31D286E848227329C37E9ADCB4B', 'BE5760FE90B4E31D286E848227329C37E9ADCB4B'], 'count': 1, 'no_user_id': 0, 'imported': 0, 'imported_rsa': 0, 'unchanged': 1, 'n_uids': 0, 'n_subk': 0, 'n_sigs': 0, 'n_revoc': 0, 'sec_read': 1, 'sec_imported': 0, 'sec_dups': 1, 'not_imported': 0, 'stderr': 'gpg: WARNING: unsafe permissions on homedir \'/home/practical_cryptography_series/pgp_approach/pgp_flow/resources/pgp_keys3\'\n[GNUPG:] IMPORT_OK 0 BE5760FE90B4E31D286E848227329C37E9ADCB4B\n[GNUPG:] KEY_CONSIDERED BE5760FE90B4E31D286E848227329C37E9ADCB4B 0\ngpg: key 27329C37E9ADCB4B: "Sender <sender@bank.com>" not changed\n[GNUPG:] KEY_CONSIDERED BE5760FE90B4E31D286E848227329C37E9ADCB4B 0\ngpg: key 27329C37E9ADCB4B: secret key imported\n[GNUPG:] IMPORT_OK 16 BE5760FE90B4E31D286E848227329C37E9ADCB4B\ngpg: Total number processed: 1\ngpg:              unchanged: 1\ngpg:       secret keys read: 1\ngpg:  secret keys unchanged: 1\n[GNUPG:] IMPORT_RES 1 0 0 0 1 0 0 0 0 1 0 1 0 0 0\n', 'data': b'', 'returncode': 0}
2025-12-02 11:46:55 | INFO | PGP-AUDIT | key imported successfully
2025-12-02 11:46:55 | INFO | PGP-AUDIT | Starting encryption for file: bank_data.txt
2025-12-02 11:46:56 | INFO | PGP-AUDIT | Encryption Successful : True
2025-12-02 11:46:56 | INFO | PGP-AUDIT | SIG_CREATED: [GNUPG:] SIG_CREATED S 1 10 00 1764661615 BE5760FE90B4E31D286E848227329C37E9ADCB4B

(venv3) root@aa8e6a4afaaa:/home/practical_cryptography_series/pgp_approach/pgp_flow# python main.py
Enter the operation to perform: 1 for key generation, 2 for encryption, 3 for decryption: 3
2025-12-02 11:48:37 | INFO | PGP-AUDIT | Importing key from resources/receiver_private.asc
2025-12-02 11:48:37 | INFO | PGP-AUDIT | Key imported successfully
2025-12-02 11:48:37 | INFO | PGP-AUDIT | Importing key from resources/sender_public.asc
2025-12-02 11:48:37 | INFO | PGP-AUDIT | Key imported successfully
2025-12-02 11:48:37 | INFO | PGP-AUDIT | Starting decryption for file: bank_data.txt.pgp
2025-12-02 11:48:38 | INFO | PGP-AUDIT | Decryption Successful True
2025-12-02 11:48:38 | INFO | PGP-AUDIT | Signature Valid : True
2025-12-02 11:48:38 | INFO | PGP-AUDIT | Signed By: Sender <sender@bank.com>
2025-12-02 11:48:38 | INFO | PGP-AUDIT | Finished decryption for file: bank_data.txt.pgp

In the above trace, the following operations are done in order:

  1. Generating 2 sets of Key Pairs: 1 for Sender and 1 for Receiver
  2. Encryption of file using Receiver’s Public Key and Signature Generation using Sender’s Private Key.
  3. Decryption of file using Receiver’s Private Key and Signature verification using Sender’s Public Key.

The source code is provided in Github .

5. PGP vs AES vs Digital Signatures — How Do They Compare?

Modern secure communication uses a mix of cryptographic tools, and it’s common to see confusion around how PGP, AES, and digital signatures differ. Each solves a different problem, and understanding their roles helps you choose the right approach for your use case.

5.1 What PGP actually is?

PGP (Pretty Good Privacy) is not just one algorithm. It is a hybrid encryption protocol that uses:

  • Symmetric encryption (e.g., AES) for speed
  • Asymmetric encryption (e.g., RSA, ECC) to protect the symmetric key
  • Digital signatures for authenticity
  • Keyrings, trust models, and fingerprints for identity management

PGP is a system, not a single cryptographic primitive.

5.2 PGP vs AES

AES

AES alone is a symmetric encryption algorithm. It Secures data but does not validate sender identity.

Where AES excels

  • Encrypting large volumes of data
  • High-performance systems
  • Databases, API payloads, streams, backups

Where AES falls short

  • Key exchange: both sides must share the same AES key securely
  • No digital signature or sender authenticity
  • No built-in key management or trust model

How PGP solves this

PGP generates a random AES session key → encrypts data → then encrypts the session key using the receiver’s public key. Now:

  • Sender doesn’t need to exchange AES key securely
  • Receiver decrypts AES key using their private key
  • Sender can also sign the encrypted message

So, PGP uses AES internally — but adds identity + trust + key exchange.

5.3 PGP vs Digital Signatures

Digital signatures alone (RSA, ECDSA, Ed25519):

  • Provide integrity
  • Provide authenticity
  • Do not provide confidentiality

A digital signature guarantees who created the data and that it was not changed — but the data is still readable by anyone.

Example: Software packages signed before distribution.

Where purely digital signatures are used

  • Code signing (Java, Python packages, OS updates)
  • Transaction signing (API requests)
  • Blockchain (Ethereum, Bitcoin)
  • Document signing (PDF signing)

Where signatures fall short

  • They cannot encrypt data
  • They do not hide content
  • They do not handle key exchange

How PGP solves this

PGP combines:

  • Digital signature → to authenticate the sender
  • Encryption → to ensure confidentiality

So you get both:

  • I know who sent it.
  • Only the intended recipient can read it.

This dual guarantee is why PGP is dominant in secure file exchange and email encryption.

5.4 A Comparison Table

PGP vs AES vs Digital Signature Comparison

PGP vs AES vs Digital Signature Comparison

6. When to use what?

Use PGP when:

  • Exchanging files between two organizations
  • Need encryption + signature in one package
  • Need long-term keyrings and trust models
  • Working with email encryption (OpenPGP/MIME)

Use AES when:

  • You control both sides of the system
  • Need extremely fast encryption
  • Working with APIs, microservices, or databases
  • Data is encrypted at rest (DB, S3, backups)

Use Digital Signatures when:

  • Need to validate integrity & identity
  • Do not need to encrypt the content
  • Signing API calls, transactions, software packages

7. Pros and Cons of PGP

PGP (Pretty Good Privacy) has been a cornerstone of secure file and email exchange for decades. Like any technology, it comes with strengths and limitations. Understanding both helps organizations and developers make informed choices.

Pros of PGP

1. End-to-End Security

  • PGP provides confidentiality via encryption.
  • Only the intended recipient with the private key can read the message.

2. Sender Authentication & Integrity

  • PGP uses digital signatures to ensure the sender’s identity.
  • Recipients can detect tampering, ensuring data integrity.

3. Hybrid Encryption for Efficiency

  • Combines symmetric (AES) + asymmetric (RSA/ECC) encryption.
  • Fast enough for large files while still secure for key exchange.

4. Platform-Independent

  • Works on Linux, Windows, macOS, and even mobile devices.
  • Interoperable across different systems and email clients.

5. No Central Authority Required

  • Unlike TLS or PKI, PGP can be used without a centralized CA.
  • Trust is established via web-of-trust or manual key verification.

6. Proven and Widely Adopted

  • Decades of use in secure email, file transfer, and enterprise systems.
  • Open standards and multiple implementations (GPG, OpenPGP).

Cons of PGP

1. Key Management Complexity

  • Users must safely store private keys and share public keys.
  • Lost keys or forgotten passphrases can permanently block access.

2. Usability Challenges

  • For non-technical users, importing keys, verifying signatures, and handling encrypted files can be confusing.
  • Mistakes in key usage can compromise security.

3. Scalability Issues

  • For organizations exchanging files with many partners, managing multiple keys becomes cumbersome.
  • Revocation and rotation of keys require careful planning.

4. No Forward Secrecy

  • If a private key is compromised, all previous messages encrypted with that key can be decrypted.
  • Modern protocols like TLS with ephemeral keys provide better forward secrecy.

5. Metadata Exposure

  • File sizes, sender/receiver identifiers, and timestamps are not encrypted.
  • PGP protects content but not necessarily communication patterns.

6. Integration Complexity

  • Integrating PGP into automated pipelines, APIs, or cloud environments often requires additional tooling or wrapper libraries.

8. Summary

In this blog, we explored PGP (Pretty Good Privacy) — a tried-and-tested approach for securing files and messages:

  1. Introduction to PGP — We saw how it combines symmetric encryption (AES) for speed, asymmetric encryption (RSA/ECC) for key exchange, and digital signatures for authenticity.
  2. How PGP Works — We broke down the workflow: key generation, signing, encryption, decryption, and signature verification.
  3. Real-World Example — Using a file transfer scenario, we demonstrated PGP in action, including the hybrid encryption approach in Python.
  4. Comparison with Other Approaches — We compared PGP with AES and standalone digital signatures, highlighting when each approach is appropriate.
  5. Pros and Cons — We discussed PGP’s strengths in confidentiality, authenticity, and interoperability, as well as challenges in key management, usability, and scalability.
  6. Code Walkthrough — We explored Python-based modular classes for generating keys, encrypting, signing, decrypting, and verifying signatures, including common pitfalls in using python-gnupg.

Key takeaway: PGP is a robust and versatile solution for secure file and message exchange, but operational excellence depends on proper key management, automation, and audit practices.

This is part of a series of blog on Secure File Transfers . Below are the links to my earlier blogs .

  1. Securing File Transfers with SHA-256: A Simple Yet Effective Approach
  2. Beyond Checksums: Securing File Transfers with HMAC-SHA256
  3. From Hashes to Signatures: Securing File Transfers with RSA/ECDSA Digital Signatures
  4. End-to-End File Encryption with AES and Integrity Checks

메타데이터
post_id
baaadd897a1c
slug
understanding-pgp-pretty-good-privacy-baaadd897a1c
url
https://medium.com/@raditya.mit/understanding-pgp-pretty-good-privacy-baaadd897a1c
canonical_url
https://medium.com/@raditya.mit/understanding-pgp-pretty-good-privacy-baaadd897a1c
author_url
https://medium.com/@raditya.mit
status
ok
fetched_at
2026-07-26 23:38:14