Vanity GPG Keys

- 6 mins read

If you haven’t known what a GPG key is, please close this tutorial.

As we all know, GPG keys have their IDs, they are essentially 40 hexadecimal characters that are used to identify your keys.

Your ID may look like this.

9684 79A1 AFF9 27E3 7D1A  566B B569 0EEE BB95 2194 // GitHub web-flow signing

Usually we just memorize the last 16 characters because it is enough in most cases.

But it’s still hard to remember, is it?

However, my key ID looks like this.

04B8 7733 FCCF 034F AB9C  E538 18BD AAAA AAAA AAAA

The last 16 characters are much easier to memorize, right?

And I guess that’s why you are reading this post.


As I mentioned above, it’s easier to memorize. Also, isn’t it a kind of vanity? XD

How it works?

GPG key IDs are calculated from your public key, it’s not customizable. Thus we need to keep generating public keys, aka brute-force it.

How to

Since it involves a LOT of calculations, we need a powerful computer. The more characters you want, the more calculations you need,

If you have ever done any kind of cryptocurrency mining, you will know that we can use either CPU or GPU to do that. The more cores it has, the more computational power you will have.

In this case, I will show both ways. But trust me, you won’t want to use CPU.


We are going to make this tutorial in a Debian GNU/Linux 12 (bookworm) system.

Huge thanks to GitHub - RedL0tus/VanityGPG: A simple tool for generating and filtering vanity GPG keys, c0nCurr3nt1Y for making this project.

This program is written in Rust, so we will need to install some dependencies.

sudo apt update
sudo apt install git rustc cargo clang make pkg-config nettle-dev libssl-dev capnproto libsqlite3-dev

Now install the program.

cargo install vanity_gpg
cargo install vanity_gpg --locked # If the first command fails.

Now we can do the calculation.

~/.cargo/bin/vanity_gpg -c Ed25519 -j$(nproc) -u "First Name <[email protected]>" -p "9{12}$|E{12}"$

-c Ed25519 indicates that we are going to generate Ed25519 keys, Cv25519 keys are currently not available. Ed25519, RSA2048/3072/4096 and NISTP256/384/521 are currently available.

-u “XXXXX” means your user ID, change as you wish.

-j$(nproc) means to utilize all your cores.

-p means pattern, after that is a regex expression, change as you wish.

CPU Jobs / Cores Hash Speed
AMD Ryzen 7 5825U 8 95,000,000 hashes/s
Neoverse N1 (ARM64) 4 30,000,000 hashes/s
Intel Core i7-8569U 8 18,000,000 hashes/s
Intel Xeon E3-1231 v3 8 15,000,000 hashes/s
AMD Ryzen 5 3600 12 80,000,000 hashes/s
AMD Ryzen 7 3700X 16 120,000,000 hashes/s
Intel Xeon Gold 6133 4 17,000,000 hashes/s
Apple M1 8 32,000,000 hashes/s

Above are some of the common CPUs.

If you have ever done some calculations, it can be slow, and a 12-character key ID can take dozens of hours.

That’s why we need to use GPUs.


Thanks to GitHub - cuihaoleo/gpg-fingerprint-filter-gpu: Generate OpenPGP keys with fingerprints that match a specific pattern (a.k.a. vanity keys) for making this project.

To do this, you must have an NVIDIA GPU with CUDA.

sudo apt update \
&& sudo apt install libgcrypt20-dev \
&& git clone \
&& cd gpg-fingerprint-filter-gpu \
&& make

You need to have CUDA installed already.

After compiling the program, we can start calculating.

./gpg-fingerprint-filter-gpu -a ed25519 -t 31104400 'x{12}' output.gpg

-a ed25519 means we can generate Ed25519 keys. And ed25519, cv25519, rsa2048/3072/4096, and nistp256/384/521 are available.

-t 31104400 means the time interval. The program generates keys more efficiently by changing the date of the past, this argument indicates we offset the time for 31104400 (unknown unit). Don’t change this unless you know what you are doing.

After that are just a regex expression and the output file.

GPU Hash Speed
Tesla T4 3,200,000,000
RTX 3090 10,000,000,000
Tesla V100 8,600,000,000
Tesla A100 12,000,000,000

Yes, I am serious. Over ONE BILLION hashes per SECOND.

You should ALWAYS use a GPU if possible.

Combining the keys.

If you generated your keys using Vanity GPG, the CPU calculator. Then the public and private keys may already in your working directory, available to use.

But if you are using GPU, the program only generates the key, and leaves no User-IDs, aka unusable now.

We need to create user-ids for them.

Let’s assume you already have a safe environment for your combining process and you have 2 Ed25519 keys and 1 Cv25519 keys, each responsible for Certify/Sign, Authentication, and Encrypt.

Import all of your keys to your GPG. This is suggested to be done in a Live USB.

gpg --allow-non-selfsigned-uid --import FILENAME.GPG

Then edit your key.

$ gpg --edit-key KEY_FINGERPRINT
gpg> adduid
-- snip --
gpg> uid 1 # Select the empty UID and delete it.
gpg> deluid
gpg> save

Since Cv25519 cannot be used as primary key, you need to merge the generated key with an existing key:


  1. Primary key should be created earlier than subkey.
  2. To persevere the subkey fingerprint, you need perserve the subkey creation time.
gpg -k --with-colons # You can use `grep` to explicitly filter out other keys.

In this case, 1669444943 is the timestamp we need to memorize.

gpg -k --with-keygrip

Write down the Keygrip for the Subkeys and we will need it later.

Now we can start to merge our keys.

First we are going to do master key.

gpg --expert --faked-system-time="TIMESTAMPHERE\!" --ignore-time-conflict --edit-key MASTER_KEY_ID

The following is a full example.

$ gpg --expert --faked-system-time="1642330159\!" --ignore-time-conflict --edit-key A364444444444444
gpg (GnuPG) 2.3.7; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: WARNING: running with faked system time: 2022-01-16 10:49:19
Secret key is available.

sec  ed25519/A364444444444444
     created: 2021-10-04  expires: never       usage: SCA
     trust: unknown       validity: unknown
[ unknown] (1). Example <[email protected]>

gpg> addkey    # Add subkeys
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
  (14) Existing key from card
Your selection? 13 # Import from the existing key
Enter the keygrip: 9195F96C656FB6D919D42D8FAB1A27ACD8E2C0ED # Keygrip of your subkeys which you have obtained earlier.

Possible actions for this ECC key: Encrypt
Current allowed actions: Encrypt

   (S) Toggle the encrypt capability
   (Q) Finished

Your selection? Q    # Done
Please specify how long the key should be valid. # Expiry date
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
Really create? (y/N) y

sec  ed25519/A364444444444444
     created: 2021-10-04  expires: never       usage: SCA
     trust: unknown       validity: unknown
ssb  cv25519/CF88999999999999
     created: 2022-01-16  expires: never       usage: E
[ unknown] (1). Example <[email protected]>

gpg> save    # Save

Similarly, you can import your authenticate key, encrypt key, signing key, etc.

And… You are essentially done!


This is pure just for fun. Now you can go flex with your friends.


某科学的 PGP 算号指南; Dejavu’s Blog, archived in Frankapository on March 24, 2024.