68

My gnupg keyring contains hundreds of unnecessary entries. How would I remove the expired, revoked and unsigned keys from it?

I would like to keep the keys that have signed my key, and import new keys only as required. I'd previously imported the entire web of trust for my key's signatories. It would seem that one of my key signers has picked up a very large number of signatures in his travels, and these are now clogging up my keyring.

5
  • 1
    I would use gpgtools.org. It does not help to remove expired, though, but definitely eases management of gpg keys
    – Nick Roz
    Jul 11 '16 at 15:01
  • The title does not match with what you want. You said "clean up my gnupg keyring", that means delete all keyring and the content said you only want to removed expired, revoked and unsinged keys.
    – MaXi32
    Sep 4 at 16:19
  • @MaXi32 - that's not what "clean up" means at all.
    – scruss
    Sep 4 at 17:16
  • I mean the title should say "How to remove expired, revoked and unsigned keys". I keep getting into your post from Google when I search "How to remove all gpg keys".
    – MaXi32
    Sep 4 at 17:47
  • It's been up for 8 years with no complaints until today. “How to remove all gpg keys” is rm -r ~/.gnupg. This will remove your private keys too, but you did say all
    – scruss
    Sep 4 at 20:36
70

From Charles Lockhart’s GPG Cheat Sheet:

I've used User Name as being the name associated with the key.  Sorry that isn't very imaginative.  I think gpg is pretty wide in its user assignments, e.g., the name for my private key is “Charles Lockhart”, but I can reference that by just putting in “Lockhart”.  That doesn't make any sense, sorry.

            ︙

to delete a public key (from your public key ring):

$ gpg --delete-key "User Name"

This removes the public key from your public key ring.
NOTE: If there is a private key on your private key ring associated with this public key, you will get an error! You must delete your private key for this key pair from your private key ring first.

to delete a private key (a key on your private key ring):

$ gpg --delete-secret-key "User Name"

This deletes the secret key from your secret key ring.

4
  • 2
    Thanks; that's a start, at least. I can use it to find and list the expired keys: gpg --list-keys | awk '/^pub.* \[expired\: / {id=$2; sub(/^.*\//, "", id); print id}' | fmt -w 999 | sed 's/^/gpg --delete-keys /;'
    – scruss
    May 11 '13 at 14:05
  • 1
    "User Name" can also be the short public key
    – nmz787
    Sep 17 '18 at 20:42
  • 1
    On gpg 2.2.20, the --delete-key and --delete-secret-key commands don't exist anymore. Jun 1 '20 at 9:51
  • @PaulRazvanBerg - I'm on 2.2.21 and --delete-secret-key just worked fine for me May 26 at 15:50
9

I have a bash script scheduled to run weekly from cron to handle this:

#!/bin/bash
# Clean up the GPG Keyring.  Keep it tidy.
# blog.lavall.ee
 
echo -n "Expired Keys: "
for expiredKey in $(gpg2 --list-keys | awk '/^pub.* \[expired\: / {id=$2; sub(/^.*\//, "", id); print id}' | fmt -w 999 ); do
    echo -n "$expiredKey"
    gpg2 --batch --quiet --delete-keys $expiredKey >/dev/null 2>&1
    if [ $? -eq 0 ]; then
        echo -n "(OK), "
    else
        echo -n "(FAIL), "
    fi
done
echo done.

echo -n "Update Keys: "
for keyid in $(gpg -k | grep ^pub | grep -v expired: | grep -v revoked: | cut -d/ -f2 | cut -d' ' -f1); do
    echo -n "$keyid"
    gpg2 --batch --quiet --edit-key "$keyid" check clean cross-certify save quit > /dev/null 2>&1
    if [ $? -eq 0 ]; then
        echo -n "(OK), "
    else
        echo -n "(FAIL), "
    fi
done
echo done.

gpg2 --batch --quiet --refresh-keys > /dev/null 2>&1
if [ $? -eq 0 ]; then
    echo "Refresh OK"
else
     echo "Refresh FAIL."
fi
3
  • 1
    What is $2 in the awk section of your script ? And while at it, what is $1 ? Cheers.
    – Cbhihe
    Apr 26 '17 at 17:10
  • Normal awk syntax for second and first (space-separated) field in the current line of input.
    – fche
    Dec 11 '17 at 18:09
  • I don't understand why doing local cleanup of keys and then… downloading all the stuff you just deleted from keyserver again with --refresh-keys. I'd put the refresh in the middle of the script, not the end.
    – lapo
    Feb 8 '18 at 8:46
4
% gpg --edit-key KEYID
gpg> clean
User ID [...]: 139 signatures removed
gpg> save
% gpg --version
gpg (GnuPG) 1.4.18
[...]
2
  • 1
    When I follow those steps and then check with ` gpg --list-keys` I keep seing the same old expired keys...
    – Cbhihe
    Apr 26 '17 at 16:54
  • 6
    gpg clean doesn't remove expired keys, only expired signatures
    – fche
    Jul 11 '17 at 1:51
4

The man page of gpg advises against parsing the normal output of --list-keys but it offers parseable output with --with-colons.

This removes all public keys that are expired or revoked:

gpg --list-keys --with-colons \
  | awk -F: '$1 == "pub" && ($2 == "e" || $2 == "r") { print $5 }' \
  | xargs gpg --batch --yes --delete-keys

How does it work?

  1. tell gpg to output keys in a parsable format (the docs for this format are a little hidden: the man page says they are in doc/DETAILS in the source, my distro installed them in usr/share/doc/gunpg/DETAILS)
  2. tell awk to use colon as field delimiter: -F:
  3. only select lines where the first field is "pub", these are public keys
  4. only select lines where the second field is either "e" or "r", these are revoced or expired keys
  5. print only field five of the selected lines, that is the key id
  6. use xargs to hand the lines to gpg and delete them
2
  • 1
    Perhaps replace --delete-keys with --delete-secret-and-public-key to get everything.
    – Jortstek
    Apr 7 at 22:54
  • @MaXi32, I just looked at my local man page and it does list that option.
    – trash80
    Sep 8 at 20:08
1
#!/bin/bash
echo -n "Expired Keys: "
list_expired_keys="$(gpg2 --list-keys | grep -1 pub | sed 'N;s/\n/ /' | awk '/^pub.* \[expired\: / {id=$7; sub(/^.*\//, "", id); print id}' | fmt -w 999)";
list_revoked_keys="$(gpg2 --list-keys | grep -1 pub | sed 'N;s/\n/ /' | awk '/^pub.* \[revoked\: / {id=$7; sub(/^.*\//, "", id); print id}' | fmt -w 999)";
for key in $list_expired_keys $list_revoked_keys; do
    echo -n "$key"
    gpg2 --batch --quiet --delete-keys $key >/dev/null 2>&1
    if [ $? -eq 0 ]; then
        echo -n "(OK), "
    else
        echo -n "(FAIL), "
    fi
done
echo done.

here is a bash-script doing the job. It is an adaption of https://superuser.com/a/859739 for gpg2 where the key id is in the second line.

1
  • pipein to fmt -w 999 is not needed.
    – jarno
    Dec 15 '19 at 0:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.