To main content

Fixing old SHA1-infested OpenPGP keys

Published by Benjamin Marwell on

I recently created a new OpenPGP key for my Apache (ASF) account. Of course I wanted to sign it with my existing GnuPG key I have since 2007. To my surprise, it failed with these error messages:

gpg: Note: third-party key signatures using the SHA1 algorithm are rejected
gpg: signing failed: Invalid digest algorithm
gpg: signing failed: Invalid digest algorithm

It took me a few hours to figure out what’s wrong. Obviously something with SHA1, but GnuPG doesn’t tell you WHAT is wrong and HOW to fix it.

The symptom: Keys containing SHA1-Packets

Between 2009 and 2012, when SHA1 was still accepted (and mandatory for GnuPG, although it had first cracks), I wrote a few pages about how [not] to configure GnuPG even more securely (or so I thought).

I included options like export mode, disabled some stuff like photos, enabled dsa2 (new at that time) etc.

Sadly, as time passed, I never reverted those options to the default. If I did, prolonging my key would have cleaned my key from SHA1 self-signatures and other SHA1 packets because the defaults changed.

Lessons learned: I will now stick to the default settings more closely and configure only obvious things like the keyserver url, displaying the whole fingerprint, disabling auto-key-locating and enabling clean on import.

Analyzing if your key is affected

Symption 1: You cannot sign keys anymore

If you try to sign a key and this happens, you might be affected:

(user@host)[~]$ gpg --sign-key bmarwell@apache.org

sec  ed25519/111133334567ABCD
     created: 2020-11-20  expires: 2027-11-19  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1). Bob Brick <bob@brick.invalid>


sec  ed25519/111133334567ABCD
     created: 2020-11-20  expires: 2027-11-19  usage: SC
     trust: ultimate      validity: ultimate
 Primary key fingerprint: 0000 2222 4444 6666 8888  AAAA 1111 3333 4567 ABCD

     Bob Brick <bob@brick.invalid>

This key is due to expire on 2027-11-19.
How carefully have you verified the key you are about to sign actually belongs
to the person named above?  If you don't know what to answer, enter "0".

   (0) I will not answer.
   (1) I have not checked at all.
   (2) I have done casual checking. (default)
   (3) I have done very careful checking.

Your selection? (enter '?' for more information): 3
Are you sure that you want to sign this key with your
key "Adam Average <adam@average.invalid>" (826D3E320E691DD1)

I have checked this key very carefully.

Really sign? (y/N) y
gpg: Note: third-party key signatures using the SHA1 algorithm are rejected
gpg: signing failed: Invalid digest algorithm
gpg: signing failed: Invalid digest algorithm

Key not changed so no update needed.

Symptom 2: Check it’s packets

To check for SHA1 sigs, you need to use gpg’s --list-packets. As it will only analyse binary or armored gpg files, you need to export the key first. Then, look for digest algorithm SHA1 (id 2) like so:

gpg --export <key-fingerprint> | gpg --list-packets | grep -B2 "digest algo 2"

If you see your key ID on the far right end of the "signature" lines (two lines before digest algo, hence the -B2), your key is polluted with self-signed SHA1 sigs. If it is not your key ID, you can just clean your key. On no output: You shouldn’t be here, you are not affected. 😉

Cleaning the key

Now is a good time to make a backup and also test your restore abilities.

Step 1: Checking your gpg.conf

If you use GnuPG 2.2 or higher, you just need to make sure you do not include any of the following options in your $HOME/.gnupg/gpg.conf file:

  • weak-digest
  • default-preference-list
  • personal-cipher-preferences
  • personal-digest-preferences
  • personal-compress-preferences
  • digest-algo
  • cert-digest-algo

If you use GPG <= 2.1.x, then set these options explicitly to not include SHA1. As I have given bad suggestions in the past, look up on the internet a good setting.

Step 2: Re-set your expiry date

You can skip this test if you have no digest algo 2 signature with your own key ID from the previous step.

Even if you haven’t set an expiry date, this step will make to re-set the expiry (either by re-typing the same exact date, setting a new interval or setting 0=never again).

# if you had 'never'
gpg --quick-set-expire <key-fingerprint> 0 '*'

# if you want to set it to the same date, here: Nov 1st, 2020
gpg --quick-set-expire <key-fingerprint> 2022-11-01 '*'

# if you want to set it to a new interval from now, here: 5y
gpg --quick-set-expire <key-fingerprint> 5y '*'

The trailing '*' is important, including the single quotes. It will make the expiry apply to all subkeys if you have multiple.

Step 3: Cleaning your key

Now it is time to actually update your key’s preferences and remove all the unwanted SHA1 signatures, whether they are your own old SHA1 signatures or third-party signatures. This is also a one-liner.

# update the preferences on your key, then
# clean your key: get rid of expired and/or invalid (SHA1) sigs
gpg --batch --yes --edit-key <key-fingerprint> setpref clean quit

Step 4: Send your keys to the keyservers (optional)

Now it is time to publish your new key if you want to:

# publish to the keyservers
gpg --keyserver keys.gnupg.net --send-keys <key-fingerprint>

Conclusion

After four simple steps, you can now use your GnuPG-Key again to sign other keys.

(user@host)[~]$ gpg --sign-key bmarwell@apache.org

sec  ed25519/111133334567ABCD
     created: 2020-11-20  expires: 2027-11-19  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1). Bob Brick <bob@brick.invalid>


sec  ed25519/111133334567ABCD
     created: 2020-11-20  expires: 2027-11-19  usage: SC
     trust: ultimate      validity: ultimate
 Primary key fingerprint: 0000 2222 4444 6666 8888  AAAA 1111 3333 4567 ABCD

     Bob Brick <bob@brick.invalid>

This key is due to expire on 2027-11-19.
How carefully have you verified the key you are about to sign actually belongs
to the person named above?  If you don't know what to answer, enter "0".

   (0) I will not answer.
   (1) I have not checked at all.
   (2) I have done casual checking. (default)
   (3) I have done very careful checking.

Your selection? (enter '?' for more information): 3
Are you sure that you want to sign this key with your
key "Adam Average <adam@average.invalid>" (826D3E320E691DD1)

I have checked this key very carefully.

Really sign? (y/N) y

(user@host)[~]$

Sweet! 😊

References

This article is loosely based on the article of heise.de Aufpoliert: Alte PGP-Schlüssel mit neuen Signaturen. I changed the commands to non-interactive, use different explanations and separated the steps into their own headings, marking the second step as optional.

It is a shame that this article was not available in English before, not even on the GnuPG website!

I hope it helps you and κῦδος to heise.de for their great article!