Clone
2
You Wouldn't Download A (Private) Key
tototo31 edited this page 2026-06-24 22:04:59 +00:00

Written by: Tototo31

You're just putting the final touches on your announcement to the world. Sipping your warm tea and winding down for the evening while you catch up on the latest happening on your favorite forums.

The feds are at the door. Next thing you know, what you used to call your door is now simply splinters on the floor. You just finished signing your message to the world so that everyone knows it came from you. You upload your announcement to your blog, pull your PGP card, and dip. Computer's burned. Location's burned. Keys Secure. Man, what poor timing for unwanted visitors.

Extreme scenario? Maybe. But whether you're protecting a pseudonymous identity, signing software releases, or simply reducing your attack surface, isolating cryptographic keys in hardware dramatically changes the risk a compromised workstation can pose to you.


Background

A PGP card is a physical hardware device used to store the private keys of a PGP key pair. This enables you to gate your encryption, authentication, and signing operations behind physical access to a token as opposed to logical file-based access.

Traditional software-based PGP keys are typically stored somewhere on disk. Malware, unauthorized file system access, or accidental backup exposure can result in complete private key compromise.

PGP cards mitigate these risks by keeping private keys inside tamper resistant hardware. The host computer never directly accesses the private key material; instead cryptographic operations are performed on the card itself and only the resulting signatures or decrypted plain-text are returned.

These physical tokens run applets that mediate access to private key operations. As a result, interacting with them requires several software layers and can take a little more effort to configure than traditional file-based keys. This is the case regardless of interface. Some smart cards have USB, some NFC, some use contact pads on the physical card they are all abstracted to GnuPG through pcscd and appear effectively the same.

PGP cards have slots for 3 private keys. One for signing, one for encrypting, and one for authentication. Sometimes Yubikeys can have an extra 4th slot.

The standard hardware for OpenPGP consists of:

  • A pgp compatible smart card. This is where private keys will be physically stored. Yubikeys are a great off the shelf solution.
  • PC/SC compatible card reader. This is important because PC/SC is the common api to talk to our smart cards. This abstraction means GnuPG does not need to know whether you're using a USB Yubikey, NFC implant, or contact smart card; they all appear through the same interface.
  • A PC.

The main software components of this setup are pcscd and GnuPG.

  • pcscd is the interface that GnuPG uses to interface with smart cards. PGP cards(including yubikeys) are a type of smart card so we have to set up the PC/SC interface regardless of reader or smart card interface.
  • GnuPG(GNU privacy guard) is the GNU implementation of open PGP. Its the primary OpenPGP implementation used on the Linux platform and facilitates access.

The general physical access stack is: `Smart Card -> PC/SC card reader -> PC with PCSC drivers

When you send a card command from GnuPG the data goes: gnupg console -> gnupg sub-components -> pcscd -> PC/SC card reader connected to PC -> Smart card

Now with a little background you should be ready to start installing software. This tutorial has been tested on Debian 13, PopOS 24, and RaspberryPiOS Trixie


Setup + Install: pcscd

pcscd is the typical way for Linux utilities to interact with smart cards. GPG packages its own smart card interface but for simplicity sake in future smart card endeavors I'm setting it up like this.

Install the daemon + tools:

sudo apt install pcsc-tools pcscd

Ensure the daemon has started:

sudo systemctl enable --now pcscd

Validate everything is setup and installed properly:

pcsc_scan

If you see errors check out the troubleshooting section below, otherwise you are now ready to proceed to setting up GnuPG!

Troubleshooting pcsc_scan Errors

If you get an error such as the following you may need to add a new polkit rule:

$ pcsc_scan
PC/SC device scanner
V 1.7.3 (c) 2001-2024, Ludovic Rousseau <ludovic.rousseau@free.fr>
SCardEstablishContext: Access denied.

Create this file: /usr/share/polkit-1/rules.d/03-polkit-pcscd.rules with the following contents (be sure to edit the username):

polkit.addRule(function(action, subject) {
    if (action.id == "org.debian.pcsc-lite.access_pcsc" &&
        subject.user == "YOUR_USERNAME") {
            return polkit.Result.YES;
    }
});

polkit.addRule(function(action, subject) {
    if (action.id == "org.debian.pcsc-lite.access_card" &&
        subject.user == "YOUR_USERNAME") {
            return polkit.Result.YES;    }
});

If you wanted to allow a group access you could replace subject.user == "YOUR_USERNAME" with subject.isInGroup("SMARTCARD_USERS_GROUP") depending on your end goal for this setup.

This rule instructs polkit to evaluate both the requested action and the user making the request. If the attempted action matches one of the specified PC/SC actions and the request originates from the configured user, polkit returns YES, and authorizes access without additional authentication.

org.debian.pcsc-lite.access_pcsc governs access to the PC/SC daemon itself, while org.debian.pcsc-lite.access_card controls access to connected smart cards. GnuPG, pcsc_scan, and other smart card utilities require authorization for these actions in order to interact with readers and cards through pcscd.

Validate functionality now:

pcsc_scan

The error should no longer pop up, and you should see your card reader recognized. If you connect/disconnect a card you should also see that pop up in this utility. You are now ready to proceed to setting up gnupg.


Setup + Install: GnuPG

GnuPG relies on scdaemon for smart card access alongside pcscd in our use case. This is why we'll need to de-conflict drivers in the next step.

sudo apt install gnupg scdaemon

Both pcscd and GnuPG's scdaemon are capable of talking directly to smart cards. If both attempt to control the reader simultaneously, GnuPG may fail to detect the card even though pcsc_scan succeeds.

To avoid this conflict, instruct scdaemon to defer hardware access to pcscd:

echo "disable-ccid" >> ~/.gnupg/scdaemon.conf

restart affected sub-components:

gpgconf --kill scdaemon
gpgconf --kill gpg-agent

Plug in your card and verify it is recognized:

gpg --card-status

PGP Card OPSec

Before we begin setting up the card itself it's important to understand the consequences that improperly securing or failing to build redundancy into your key infrastructure can pose.The next sections step through some important considerations to make before committing fully to this solution.

Master Keys

OpenPGP supports a hierarchy of keys. The Master Key acts as the root of trust and is primarily used to certify subkeys and identities.

The Master Key should ideally never touch an internet-connected system after initial generation.

It is best practice to keep the Master Key offline. It should be stored on encrypted removable media or in cold storage. Only dedicated subkeys should be loaded onto the hardware token:

  • [S] Signing subkey
  • [E] Encryption subkey
  • [A] Authentication subkey

This limits the impact of hardware loss. If a token is lost, the offline Master Key can revoke compromised subkeys and issue replacements without rebuilding your entire identity infrastructure.

Backups

Hardware tokens fail, are lost, or become damaged. Before relying on a
PGP card operationally, establish a recovery plan.

Recommended practices include:

  • Store an offline copy of the Master Key on encrypted removable media.
  • Create and securely archive a revocation certificate.
  • Maintain at least one spare hardware token populated with identical
    subkeys.
  • Store backups in geographically separate locations.

A hardware token should never be your only copy of critical
cryptographic material.


PGP Card Setup

Now its finally time to set up the PGP card, and load up certs. If all you have is a blank Java card or flex secure implant, or want to make your own check out the section at the end on setting them up!

Before we begin lets validate our card is still seen by running:

gpg --card-status

If you see your card and everything looks as expected lets go on to actually edit the card:

gpg --edit-card

For simplicity, this guide demonstrates generating keys directly on the card. This is suitable for many personal use cases. However, users seeking the strongest operational security should generate their Master Key offline, create dedicated subkeys, and transfer only those subkeys to the hardware token.

In the GPG Card prompt run the commands: list <- Will list the current keys on the card admin <- Will enable admin commands on the card generate <- Will generate new keys and put them on the card

If you are asked for your pin the default user pin is 123456 and the default admin pin is 12345678. I would highly recommend changing these once the rest of your configuration has completed.

Follow the prompts. Optionally set additional card details for login, cardholder name, and public key URL if you have it hosted somewhere.

login -> configure the username associated with these keys name -> configure the name of the card holder url -> configure the URL where the public key can be downloaded

If you get any weird errors double check your environment is configured according to this guide. Alternatively it is never a bad idea to disconnect/reconnect your PGP card. Also be aware you'll also run into issues if there isn't enough free space on your smart card for your keys.

Now that you have your keys its time to learn how to use them.


PGP Card Usage

Now that we have a configured PGP card lets do some tests with it. Here are some things we can do with our newly configured key.

How to export your public key

Public keys being public is half the equation for asymmetric encryption so getting them in an easily shareable format is extremely helpful.

If you want to share your public key with someone you'd export your public key like this:

gpg --armor --export <key_fingerprint> > pubkey.asc

If you want to import another users public key so you can verify or encrypt for them import their key by running:

gpg --import pubkey.asc

How to Encrypt and Decrypt a File

Now comes the actual encrypting. This is the good part.

To encrypt a file:

gpg --output <output_file_path>.gpg --encrypt --recipient "<key_fingerprint>" <input_file_path>

The recipient field should be your own key if you're encrypting something only for yourself, or it should be the key of your recipient

Now to decrypt the encrypted file we just created. Assuming we used own own key as the recipient we should be able to decrypt the file.

gpg --output secret-file.txt --decrypt secret-file.txt.gpg

Clear Sign data

Signing data allows us to prove both the authenticity and integrity of a message. If the message is modified after signing, signature verification will fail.

One of the simplest signing methods is clear signing. A clear-signed message remains completely human-readable while embedding a cryptographic signature alongside the contents.

This works really well for:

  • Emails
  • Forum posts
  • Security advisories
  • Public announcements
  • Text documents intended to be read directly

To clear-sign a message directly from standard input:

echo "Super important message here" | gpg --clear-sign > output.txt

This also works for an existing text file:

gpg --clear-sign [file]

The resulting file will contain both the original message and an ASCII armored signature block similar to the following:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Super important message here

-----BEGIN PGP SIGNATURE-----
...
-----END PGP SIGNATURE-----

This will allow us to sign our super important message to prove it came from whoever holds access to the private key while keeping the contents of the message completely readable by any party interested.

Sign Data

Sometimes we want to preserve the original file exactly as it exists while still providing proof of authenticity. Detached signatures solve this problem by generating a separate signature file rather than modifying the original data. This approach is commonly used for:

  • Software releases
  • ISO images
  • Package repositories
  • Source code archives
  • Backups

To create a detached signature for a file:

gpg --detach-sign [file]

This produces a new file named:

[file].sig

The original file remains unchanged.

For example:

gpg --detach-sign example.iso

Produces:

example.iso
example.iso.sig

Now you'll distribute these two files along side each other.

Verify Signed Data

Verifying signatures is just as important as creating them.Whether downloading software, validating a public statement, or confirming the authenticity of a document, signature verification allows us to confirm both who produced the file and whether it has been altered.To verify a clear-signed document:

gpg --verify signed-document.txt

To verify a detached signature:

gpg --verify example.iso.sig example.iso

A successful verification will produce output similar to:

gpg: Signature made Thu 18 Jun 2026 using RSA key ...
gpg: Good signature from "Alice Example <alice@example.com>"

If the file has been modified in any way after signing, verification will fail and GnuPG will warn the user.

Remember that a "Good signature" only confirms that the file was signed by the holder of the corresponding private key. You must still verify that you trust the public key used. for verification.

Sign git Commits

Who doesn't want to be signing their git commits to affirm their authenticity? With all the supply chain attacks we see having a layer of assurance the commits your committing actually come from who they say they are.

First you'll need to identify the key you want to use as the signing key in git. Do this by running:

gpg --list-public-keys

Next you'll export the public-key block for your git provider profile. Copy and paste the output of this command into your designated user profile in your git provider of choice.

gpg --armor --export <key_fingerprint>

Now well go back to setting up git. Set the signing key to the fingerprint used in the last command:

git config --global user.signingkey <key_fingerprint>!

If you want to make sure every commit you produce moving forward is signed set the following git config option:

git config --global commit.gpgsign true

Now finally to manually sign a commit:

 git commit -a -S -m 'This is signed see the -S?'

Bonus: Making your own NFC Enabled PGP Cards

This is where my exploration into PGP cards initially began so I thought it would be a shame not to include it. If you don't have a compatible security key, or simply want to learn how these keys work at a lower level here's a basic tutorial for installing the SmartPGP Java card applet onto a java card to enable PGP card functions.

Needed tools:

  • A javacard
    • J3R180 is a good one
    • Flex Secure is also a great one if you want something implantable
  • Acr1552 smart card reader - acr122u should also work
    • Should expose a PCSC compatible interface like CCID
    • You can also use a contact based reader if your card supports it.
  • A working PGP setup as configured in the previous sections

Blacklist NFC drivers if needed

Due to contactless readers occasionally also exposing a different interface to interact with the reader that is incompatible with smart card usage its recommended that you blacklist the systems default NFC Drivers

To do so in your current runtime run the following commands:

sudo modprobe -r pn533_usb
sudo modprobe -r pn533
sudo modprobe -r nfc

To make the change permanent introduce a modprobe config to blacklist the related kernel modules:

sudo touch /etc/modprobe.d/blacklist-nfc.conf
echo "blacklist nfc"       | sudo tee -a /etc/modprobe.d/blacklist-libnfc.conf > /dev/null
echo "blacklist pn533"     | sudo tee -a /etc/modprobe.d/blacklist-libnfc.conf > /dev/null
echo "blacklist pn533_usb" | sudo tee -a /etc/modprobe.d/blacklist-libnfc.conf > /dev/null

Download global platform pro

The tool global platform pro is the main interface for the java card. This allows us to manage applications on the smart card. We'll step through installing the open PGP applet which allows your java card to interact with the openpgp card standard.

Global platform pro is java based so you'll need to install a compatible JRE:

sudo apt install default-jre

Next go ahead and download Global Platform Pro from their website: https://javacard.pro/globalplatform/

Now run the application to test if you get valid output:

java -jar gp.jar -l

With a card connected this is what a successful read looks like:

# Warning: no keys given, defaulting to 404142434445464748494A4B4C4D4E4F
ISD: A000000151000000 (OP_READY)
     Parent:   A000000151000000
     From:     A0000001515350
     Privs:    SecurityDomain, CardLock, CardTerminate, CardReset, CVMManagement, TrustedPath, AuthorizedManagement, TokenVerification, GlobalDelete, GlobalLock, GlobalRegistry, FinalApplication, ReceiptGeneration

APP: F276A288BCFBA69D34F31001 (SELECTABLE)
     Parent:   A000000151000000
     From:     F276A288BCFBA69D34F310

APP: D276000124010304AFAF000000000000 (SELECTABLE)
     Parent:   A000000151000000
     From:     D27600012401

PKG: A0000001515350 (LOADED)
     Parent:   A000000151000000
     Version:  255.255
     Applet:   A000000151535041

PKG: A00000016443446F634C697465 (LOADED)
     Parent:   A000000151000000
     Version:  1.0
     Applet:   A00000016443446F634C69746501

PKG: A0000000620204 (LOADED)
     Parent:   A000000151000000
     Version:  1.0

PKG: A0000000620202 (LOADED)
     Parent:   A000000151000000
     Version:  1.3

PKG: F276A288BCFBA69D34F310 (LOADED)
     Parent:   A000000151000000
     Version:  1.0
     Applet:   F276A288BCFBA69D34F31001

PKG: D27600012401 (LOADED)
     Parent:   A000000151000000
     Version:  1.0
     Applet:   D276000124010304AFAF000000000000

If you receive any errors despite having a valid smart card daemon setup try disconnecting and reconnecting the card. If you're experiencing key issues authenticating to your card then you may have different keys than default in which case you will need to supply them.

Install the applet:

Now that global platform pro is setup we can use it to install the application onto our java card.

Go ahead and grab the latest release of the smart PGP applet from Github: https://github.com/github-af/SmartPGP

Once you have extracted the .zip archive and decided which .cap file you want to install its time to construct the command to install the applet:

java -jar gp.jar --install ~/Downloads/SmartPGP-v1.23.0-javacard-3.0.4/SmartPGP-v1.23.0-javacard-3.0.4/SmartPGPApplet-rsa_up_to_2048.cap

Your command will look very similar to mine but be sure to change the file path to point to your .cap file

Once the command finishes you should be able to use it with all the commands listed in this articles previous sections.


Troubleshooting:

pcsc_scan reports "Access denied"

Review the PolicyKit rules described earlier in this guide.

Card appears in pcsc_scan but not gpg --card-status

Ensure:

echo "disable-ccid" >> ~/.gnupg/scdaemon.conf

Restart:

gpgconf --kill scdaemon
gpgconf --kill gpg-agent

Card suddenly stops responding

Disconnect and reconnect the token, then restart:

gpgconf --kill scdaemon
sudo systemctl restart pcscd

Smart card out of storage space

Some cards cannot store RSA 4096-bit keys. Consider ECC keys or verify the storage limitations of your hardware.

Reader busy errors

Only one application can communicate with the reader at a time. Close Kleopatra, pcsc_scan, or other smart-card utilities before attempting operations.


Sources

https://gist.github.com/ageis/5b095b50b9ae6b0aa9bf

https://kevinsguides.com/guides/security/software/pgp-encryption/

https://blog.apdu.fr/posts/2023/11/pcsc-lite-and-polkit/

https://support.yubico.com/s/article/Troubleshooting-issues-with-GPG

https://www.gnupg.org/howtos/card-howto/en/smartcard-howto.html

https://wiki.debian.org/Smartcards/YubiKey4

https://keys.openpgp.org/

https://wiki.archlinux.org/title/OpenPGP_card

https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key

https://github.com/microsoft/vscode/wiki/Commit-Signing

https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work

https://github.com/martinpaljak/GlobalPlatformPro/wiki/Application-management

https://github.com/github-af/SmartPGP