23 Oct 2026 / Security
PGP Key Verification Step by Step: Fingerprints, Signatures, and Detecting Swapped Vendor Keys
A swapped public key does not look broken. It encrypts cleanly. The only person who can decrypt is the holder of the matching private key, and after a swap that holder is not the party named on the listing.
That is the whole attack. Everything else in PGP key verification is a way of noticing the swap before you type anything you would hate to see copied.
A name on a key is a label. The fingerprint is the identity.
A key fingerprint is a short string that uniquely identifies one public key. The beginner docs illustrate the shape as A1B2 C3D4 E5F6 7890 1234 5678 9ABC DEF0 1234 5678. Treat that as a format sketch, not a key to import. A real fingerprint is longer than a display name, and it is annoying to check. That annoyance is the point.
The name and email inside the key block are chosen by whoever generated the key. A phisher can put the same name on a fresh key in a minute. If you check the name and skip the fingerprint, you checked nothing.
The failure mode is a man-in-the-middle. Someone hands you a fake public key. You encrypt to it. They decrypt, read, and if they want the thread to continue they re-encrypt toward the real key. Both sides see plausible messages. KeychainPGP’s beginner notes describe that interception path, and it is the same path on a vendor profile as it is in email.
Short key IDs are a related trap. The last few hex characters are easier to glance at and easier to paste over. Compare the full fingerprint, in groups, from a local view of the key you actually imported.
Encryption and signatures answer different questions
Encryption uses the recipient’s public key. Only their private key reads the result. A signature uses your private key. Anyone with your public key can check that the signature matches and that the bytes were not altered after signing.
The usual analogy holds. Encryption is a locked box only one person can open. A signature is a wax stamp people can inspect but not forge, provided they are looking at your real stamp and not a lookalike. You can encrypt, sign, or do both. Doing both is the right default when a message needs to stay private and also needs a provenance check.
A directory announcement often only needs the stamp, because the content is meant to be public. A private note needs the lock. If you also need to prove which key sent it, sign as well.
A valid signature does not mean “this human is who they claim.” It means the bytes were produced with the private key matching the public key you just used. If that public key was swapped before you trusted it, the math still checks out. It checks out for the attacker.
Out of band, or the check is theater
Import is not verification. After you import, you compare the fingerprint with a copy the owner gives you on a separate channel: a phone call, an in-person meeting, or some other path that does not ride on the same page that delivered the key. The KeychainPGP how-to states that split explicitly. It is the step people skip because the key “imported fine.”
On a forum or a vendor profile, the page and the key usually arrive together. That is one channel. A swapped profile can show a swapped key and a fingerprint graphic that match each other. They will not match the fingerprint you stored last month from a channel you already trusted.
If you have never verified that key at all, you do not have a baseline. A first-seen key is an unverified key. This article is an audit of a cryptographic claim. It is not a workflow for sending money or placing an order. A passing signature is not permission to transact, and nothing here points at a live market.
How a swapped vendor key shows up
The annoying cases are quiet.
The display name matches. The email matches. The ASCII-armored block is well formed, from —–BEGIN PGP PUBLIC KEY BLOCK—– through —–END PGP PUBLIC KEY BLOCK—–. Your tool imports it and lists the contact. Then you read the fingerprint off the local keyring and it is not the string in your notes.
Sometimes the listing says the key was rotated. Rotation is normal. An unexplained rotation is not. A convincing rotation is signed by the previous key, and that signature verifies against the fingerprint you already stored. A new block posted next to the words “updated key” is just a new block. The old key might be lost, or the account might be in someone else’s hands. You cannot tell from the prose.
Other smells, none of them proof by themselves:
- The fingerprint on the profile changed, and nothing signed by the old key announces it.
- Two mirrors publish different fingerprints for the same name. At least one is lying, or one is stale. Do not pick the one that is easier to copy.
- A support message tells you to ignore the old fingerprint and use the key attached to that same message. The message is the attack.
- You are asked to confirm a short key ID, a screenshot, or the first and last groups only. Partial checks are how swaps survive a glance.
Keep the trusted fingerprint somewhere the listing cannot edit: a password-manager entry, a card, a file on a machine that does not auto-fetch profile pages. If the only copy of the “known” fingerprint lives on the same site as the key, you are comparing the forgery to itself.
| Torzon Market |
torzon7aphar3x4l5b77nsylgyw26kntbi4m2wemrjh72aczeh27f6qd.onion
|
| DrugHub |
drughuj7l72ig56pza77eriu7yh6qsao4xb4yasq2qfjusxzuq6rlwqd.onion
|
| DarkMatter |
darkmafmzgnsmow5z3spgludhpwxhwbg77oam433fjx3clzh2yp2oaid.onion
|
| Omega Market |
omega7yhz7n4vg4yhf2na2qaaaeatdlqvjbj2juc245mr5muxtnuvgyd.onion
|
| BlackOps |
blackoogcnxogvymmebfwfjhx4k7efpgeoeytxtsev2lc4pqlbz54qad.onion
|
Directory posts are the cleanest drill
The same check is how careful people treat link directories. A writeup on Tor.Taxi and Dark.Fail puts it in investigator terms. Those directories are widely used and still a single point of failure. Compromise the host and the .onion links on the page can be replaced with phishing links. The mitigation described there is PGP, not a second look at the logo.
The directory publishes a message that contains the new links. It signs that message with its private key. You verify the signature with the public key you already bound to that directory by fingerprint. If the signature matches, the message came from the holder of that private key. The writeup calls that mathematical certainty. Narrow the sentence. It is certainty relative to the public key you verified. Feed the verifier the attacker’s key and you get a clean bill of health for the attacker’s links.
That is why the fingerprint binding has to happen before the day the links move. Saving the key from the same page as today’s signed list, on the day you need the list, repeats the single point of failure in a new costume.
Two rules from that same operational note, and they hold. Do not use a link for anything that moves money until the signature has been checked. And do not go looking for those directories through surface-web hostnames that sit in front of them. Those front doors offer no privacy. Your ISP can see the visit. They also are not the signed channel.
I am not linking those directories. If you are researching how a phishing swap propagates, the object of study is the signature, not the destination.
Where to read the fingerprint
Read it from software that has the key locally. Do not read it from the HTML next to the download.
KeychainPGP is the concrete toolchain in the docs this walkthrough leans on. The web app loads a PGP engine compiled to WebAssembly from Rust and claims the work stays in the browser: no account, no key material posted to a server. You generate a key on the Keys tab. Current builds produce an Ed25519 signing key plus X25519 for encryption. Export the public half as ASCII armor. Import someone else’s armor the same way, by paste, or because the app noticed the clipboard. The contact shows up with the name and email from inside the block. That display is not the verification.
There is a desktop build for Windows, macOS, Linux, and Android, from their GitHub releases. It adds global hotkeys, a system tray, clipboard auto-clear, the OS credential store, and QR sync to a phone. The CLI covers generation, encrypt, decrypt, sign, verify, and keyring work if you want this in a script.
GnuPG is the older path. gpg –import contact-public-key.asc pulls an armored file into the local keyring. Then you print the fingerprint from that keyring and compare. I still prefer this when the alternative is a tab opened in a hurry. A local binary you already pinned has a smaller surprise surface than a site you re-fetch.
My reservation about the browser app is ordinary supply-chain risk, not a bug I am claiming to have found. If the page or the WebAssembly it serves is tampered with, “runs locally” does not save you. Local means those bytes did not need a server to encrypt. It does not mean the bytes are the ones the project published. For a one-time experiment, fine. For a fingerprint you will trust for months, pin a build, and keep the private key off machines you share.
Whichever tool you use, the order stays the same. Import the full armored public key. Display the fingerprint locally. Compare it to the out-of-band copy. Only then encrypt, and only to that key. An encrypt tab will happily encrypt to whatever recipient you select. The software cannot know you picked the swapped one.
Armor is packaging, not proof
A block that starts with —–BEGIN PGP MESSAGE—– is ASCII armor. Armor is how binary OpenPGP data survives email, chat boxes, and web forms. Without it you would be trying to paste raw bytes. Armor does not mean the message is authentic, and it does not mean the key in a different block is the right one. It means the text is safe to copy.
A public key, a signed message, and an encrypted message can all wear armor. Read the BEGIN line before you decide which operation you are performing. Verifying a signature is a different step from comparing a fingerprint. Do not blur them because both look like a wall of text.
The pass worth keeping
- Store the full fingerprint from a channel the listing cannot rewrite. Do it once, early, and keep that copy off the site.
- On every later use, import the armored public key yourself. Paste the whole block, not a screenshot of it.
- Read the fingerprint from your own tool. Compare every group. One character off is a different key. Stop.
- Treat an unsigned key change as hostile until a signature from the old key verifies, or until the owner repeats the fingerprint on a channel you already trust.
- For a directory announcement, verify the signature on the link list against that already-bound key. A mismatch means you do not follow the links.
- Do not confirm keys from screenshots, short IDs, or the page that supplied the key block.
Most bad checks are label checks. The name matched, the armor parsed, the tool said imported. That is not verification. Verification is a string comparison you could lose, plus a signature check against the key that string belongs to. If you cannot afford to lose the comparison, you cannot afford to send the message.