thoughts/data/gpg-openssl.md
Tommy Skaug a4adbc2b1c
All checks were successful
Export / Explore-GitHub-Actions (push) Successful in 34s
chore: adjustments to line length of code blocks
2024-08-06 16:13:47 +02:00

4.7 KiB

Key Takeaways

  • PGP are replaceable with native OpenSSL RSA public key crypto and AES-256 keys.
  • This approach simplifies crypto operations, and only requires OpenSSL which is widely available.
  • Existing PGP keys stored in GnuPG work with OpenSSL via gpgsm.

Introduction

The rabbit hole mission of mine to get rid of PGP continues.

Lately I have been looking into converting PGP keys from GnuPG to OpenSSL. This way I can send encrypted data to people not using my OpenSSL-only approach. After all, most people still depend on PGP and it is the format they publish their public keys in.

Exporting A PGP Public Key for Encryption Using OpenSSL

A PGP key cannot be directly read by OpenSSL, but GPG can natively export to SSH and ssh-keygen to PKCS8:

gpg --export-ssh-key <key-id>! > /tmp/test.pub
ssh-keygen -f /tmp/test.pub -e -m PKCS8 > /tmp/test.pem

The above pubkey can be used to encrypt data with OpenSSL as shown on my contact page:

KEY=`openssl rand -hex 32` IV=`openssl rand -hex 16`
ENCRYPTED_KEY_B64=`openssl pkeyutl -encrypt -pubin -inkey /tmp/test.pem \
                   -pkeyopt rsa_padding_mode:oaep <<< $KEY|base64`
BLOB=`openssl enc -aes-256-cfb -a -e -K ${KEY} -iv ${IV} -in some-file`
echo "PKCS11-VAULT;aes-256-cfb;rsa_padding_mode:oaep;$ENCRYPTED_KEY_B64:$IV:$BLOB;" \
      > encrypted.txt

The steps of the above are:

  1. Create an initialization vector [1] and an encryption key
  2. Encrypt the one-time key to test.pem (our exported PGP-key)
  3. Encrypt some-file using the key and IV using 256 bits AES in CFB-mode
  4. Format the output in my PV-format.

Store encrypted.txt for decryption in the next section.

Exporting a PGP Private Key for Decryption Using OpenSSL

This part is a bit more complex. For the sake of an example, let us say you received an encrypted blob with an IV and encrypted key, using the approach shown in the former section. You have the key stored in GnuPG.

gpgsm can export your private key to p12, which is readable for OpenSSL [2].

First list your secret keys in the GnuPG store: gpg --list-secret-keys --with-keygrip.

Convert the key to X.509 by: gpgsm --gen-key -o /tmp/temp.crt. You need to fill the values requested:

  • Select "existing key"
  • Fill the keygrip from the GPG secret key listing. Make sure you use the right key, since GPG generates several keys behind the scenes (the encryption key)
  • Fill the cn (this needs to be on the format "cn=...") and e-mail
  • Accept the other values as empty and accept the creation

Now import the certificate into gpgsm: gpgsm --import /tmp/temp.crt. When imported, find the key ID by: gpgsm --list-keys.

Using the key ID, you can now export the key in p12-format.

gpgsm -o /tmp/$keyid.p12 --export-secret-key-p12 $keyid
openssl pkcs12 -in /tmp/$key.p12 -nodes -nocerts|tail -n +5 > /tmp/$key.key

You only need to do the conversion once and now have your key in /tmp/$key.key. This should be secured accordingly, and have a password set as is offered in the guidance by gpgsm.

The resulting /tmp/$key.key is usable for decrypting content encrypted by the public key. To decrypt the data in encrypted.txt:

IFS=';' read IDENTIFIER ALGORITHM PADDING_MODE ENCRYPTION_BLOBS SIGNATURE < encrypted.txt

for BLOB in ${ENCRYPTION_BLOBS[@]}; do
    IFS=':' read ENCRYPTED_KEY_B64 IV TEXTFILE_ENC <<< $BLOB
    ENCRYPTED_KEY=`printf $ENCRYPTED_KEY_B64 | base64 -d`
    decrypted=false
    DECRYPTED_KEY=`echo $ENCRYPTED_KEY_B64 |base64 -d | \
                   openssl pkeyutl -decrypt -inkey /tmp/$key.key \
                     -pkeyopt ${PADDING_MODE} 2> /dev/null` && decrypted=true
    if [ $decrypted != false ]; then
        TEXTFILE_DEC=`printf %s "$TEXTFILE_ENC"|base64 -d|openssl enc \
                      -$ALGORITHM -d -K "$DECRYPTED_KEY" -iv "$IV" |base64`
        break
    fi
done

echo $TEXTFILE_DEC

The above format supports encryption to multiple parties. It:

  1. Reads the PV-format into variables
  2. Loops through the encryption blobs (one pass if one recipient)
  3. Decrypts the key with the private key generated from gpgsm
  4. Using the IV and decrypted key, decrypts the content, which is eventually the same as in the previous section's some-file
  5. Prints the decrypted content

Conclusion

It is possible to convert PGP keys to use with OpenSSL via gpgsm.

Since OpenSSL is more widely distributed and installed than GnuPG, it is a method applicable in more environments.

Using OpenSSL instead of GnuPG provides more options, and reduces the complexity of cryptography (since GnuPG has lots of options).

[1] https://stackoverflow.com/questions/39412760/what-is-an-openssl-iv-and-why-do-i-need-a-key-and-an-iv

[2] https://superuser.com/a/1414277