Using GnuPG with OpenSSL for Encryption and Decryption

November 18 2020

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.


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/
ssh-keygen -f /tmp/ -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:


for BLOB in ${ENCRYPTION_BLOBS[@]}; do
    ENCRYPTED_KEY=`printf $ENCRYPTED_KEY_B64 | base64 -d`
    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`


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


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).



Tags: #openssl #ssh #gnupg #encryption #decryption #crypto
Read with Gemini

SSH Certificates and Keys in Apple T2 On-host

October 14 2020

Key Takeaways

  • SSH certificates can be used with the Apple T2 chip on macOS as an alternative to external smart cards, authenticated with a fingerprint per session.
  • The Mac T2 chip serves as an extra security layer by creating private keys in the secure enclave.
  • The CA can be stored on an external smartcard, only signing for access in a limited period - again limiting the exposure.


Over the past days I have been going down a deep, deep rabbit hole of SSH proxy jumping and SSH certificates combined with smart cards.

After playing around with smart cards for SSH, I recognized that not only external smart cards such as the Yubikey or Nitrokey is a possible lane to go down.

Mac computers comes with a security chip called T2. This chip is also known to host something Apple calls Secure Enclave [1]. In the Secure Enclave you can store keys.

It will probably not serve as an equally secure solution as with external smart cards, but it is a better balance for usability.

The T2 is permanently stored in hardware on one host only, so the access needs to be signed on a per-host basis. In such I would say the T2 and external smart cards complement each other.

Always having the key available will bring two additional vulnerabilities:

  • If compromised, the key is always available logically
  • Separation of equipment and key is not possible e.g. in a travel situation

With a central pubkey directory tied to an identity (automated), the T2 can be of better use for an enterprise setup.

Setting up a Private Key in Secure Enclave

While fiddling around I found sekey on Github [2]. The project seems abandoned, but it is the secure enclave that does the heavy lifting.

The short and easy setup are:

$ brew cask install sekey
$ echo "export SSH_AUTH_SOCK=$HOME/.sekey/ssh-agent.ssh" >> ~/.zshrc
$ echo "IdentityAgent ~/.sekey/ssh-agent.ssh" >> ~/.ssh/config
$ source ~/.zshrc

A keypair can now be generated in the secure enclave by:

$ sekey --generate-keypair SSH
$ sekey --list-keys

Now export the public key of the curve generated on-chip:

$ sekey --export-key <id> >

Using the trick we found in our recent venture into using smart cards for signing the key, we can used PCKS#11 without compromising security [3]. In this case I use a Nitrokey:

$ brew cask install opensc
$ PKCS11_MODULE_PATH=/usr/local/lib/
$ ssh-keygen -D $PKCS11_MODULE_PATH -e >
$ ssh-keygen -D $PKCS11_MODULE_PATH -s -I example -n zone-web -V +1h -z 1
Enter PIN for 'OpenPGP card (User PIN)': 
Signed user key id "example" serial 1 for zone-web valid from 2020-10-14T20:26:00 to 2020-10-14T21:27:51
cp ~/.ssh/

If you now try to ssh into a server using the given certificate authority as shown in the SSH-CA post [3], access should be granted with a fingerprint.

A Word of Caution

The T2 has some vulnerabilities shown recently [4]. Make sure to include these in your risk assessment of using it. If you won’t go down the smart card route it will still be better than storing the key on disk.


Tags: #architecture #ssh #apple #t2
Read with Gemini

SSH Certificate Signing, Proxying and Smartcards

October 13 2020

Key Takeaways

  • SSH has a key-signing concept that in combination with a smartcard provides a lean, off-disk process
  • A SSH-CA provides the possibility of managing access without a central point of failure
  • The use of SSH Jumphost is an easier way to tunnel sessions end-to-end encrypted, while still maintaining visibility and control through a central point


This post is an all-in-one capture of my recent discoveries with SSH. It is an introduction for a technical audience.

It turns out that SSH is ready for a zero trust and microsegmentation approach, which is important for management of servers. Everything described in this post is available as open source software, but some parts require a smartcard or two, such as a Yubikey (or a Nitrokey if you prefer open source. I describe both).

I also go into detail on how to configure the CA key without letting the key touch the computer, which is an important principle.

The end-result should be a more an architecture providing a better overview of the infrastructure and a second logon-factor independent of phones and OATH.


My exploration started when I read a 2016-article by Facebook engineering [1]. Surprised, but concerned with the configuration overhead and reliability I set out to test the SSH-CA concept. Two days later all my servers were on a new architecture.

SSH-CA works predictably like follows:

                                       [ User generates key on Yubikey ]
[ ssh-keygen generates CA key ] --------> [ signs pubkey of Yubikey ]
                |                           - for a set of security zones
                |                           - for users
                |                                       |
                |                                       |
                |                                       v
                v                         pubkey cert is distributed to user
[ CA cert and zones pushed to servers ]     -
  - auth_principals/root (root-everywhere)
  - auth_principals/web (zone-web)

The commands required in a nutshell:

# on client
$ ssh-keygen -t rsa
# on server
$ ssh-keygen -C CA -f ca
$ ssh-keygen -s ca -I <id-for-logs> -n zone-web -V +1w -z 1
# on client
cp ~/.ssh/

Please refer to the next section for a best practice storage of your private key.

On the SSH server, add the following to the SSHD config:

TrustedUserCAKeys /etc/ssh/
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u

What was conceptually new for me was principals and authorization files per server. This is how it works:

  1. Add a security zone, like zone-web, during certificate signing - “ssh-keygen * -n zone-web *”. Local username does not matter
  2. Add a file per user on the SSH server, where zone-web is added where applicable - e.g. “/etc/ssh/auth_principals/some-user” contains “zone-web”
  3. Login with the same user as given in the zone file - “ssh some-user@server”

This is the same as applying a role instead of a name to the authorization system, while something that IDs the user is added to certificate and logged when used.

This leaves us with a way better authorization and authentication scheme than authorized_keys that everyone uses. Read on to get the details for generating the CA key securely.

Keeping Private Keys Off-disk

An important principle I have about private keys is to rather cross-sign and encrypt two keys than to store one on disk. This was challenged for the SSH-CA design. Luckily I found an article describing the details of PKCS11 with ssh-keygen [2]:

If you’re using pkcs11 tokens to hold your ssh key, you may need to run ssh-keygen -D $PKCS11_MODULE_PATH ~/.ssh/ so that you have a public key to sign. If your CA private key is being held in a pkcs11 token, you can use the -D parameter, in this case the -s parameter has to point to the public key of the CA.

Yubikeys on macOS 11 (Big Sur) requires the yubico-piv-tool to provide PKCS#11 drivers. It can be installed using Homebrew:

$ brew install yubico-piv-tool
$ PKCS11_MODULE_PATH=/usr/local/lib/libykcs11.dylib

Similarly the procedure for Nitrokey are:

$ brew cask install opensc
$ PKCS11_MODULE_PATH=/usr/local/lib/

Generating a key on-card for Yubikey:

$ yubico-piv-tool -s 9a -a generate -o public.pem

For the Nitrokey:

$ pkcs11-tool -l --login-type so --keypairgen --key-type RSA:2048

Using the exported CA pubkey and the private key on-card a certificate may now be signed and distributed to the user.

$ ssh-keygen -D $PKCS11_MODULE_PATH -e  >

$ ssh-keygen -D $PKCS11_MODULE_PATH -s -I example -n zone-web -V +1w -z 1
Enter PIN for 'OpenPGP card (User PIN)': 
Signed user key .ssh/ id "example" serial 1 for zone-web valid from 2020-10-13T15:09:00 to 2020-10-20T15:10:40

The same concept goes for a user smart-card, except that is a plug and play as long as you have the gpg-agent running. When the (the signed certificate of e.g. a Yubikey) is located in ~/.ssh, SSH will find the corresponding private key automatically. The workflow will be something along these lines:

[ User smartcard ] -----------> [ CA smartcard ]
         ^          |
         |                              | signs
           sends back

A Simple Bastion Host Setup

The other thing I wanted to mention was the -J option of ssh, ProxyJump.

ProxyJump allows a user to confidentially, without risk of a man-in-the-middle (MitM), to tunnel the session through a central bastion host end-to-end encrypted.

Having end-to-end encryption for an SSH proxy may seem counter-intuitive since it cannot inspect the content. I believe it is the better option due to:

  • It is a usability compromise, but also a security compromise in case the bastion host is compromised.
  • Network access and application authentication (and even authorization) goes through a hardened point.
  • In addition the end-point should also log what happens on the server to a central syslog server.
  • A bastion host should always be positioned in front of the server segments, not on the infrastructure perimeter.

A simple setup looks like the following:

[ client ]  ---> [ bastion host ] ---> [ server ]

Practically speaking a standalone command will look like follows:

ssh -J

An equivalent .ssh/config will look like:

 User sshjump
 Port 22

 User some-user
 Port 22

With the above configuration the user can compress the ProxyJump SSH-command to “ssh”.

Further Work

The basic design shown above requires one factor which is probably not acceptable in larger companies: someone needs to manually sign and rotate certificates. There are some options mentioned in open sources, where it is normally to avoid having certificates on clients and having an authorization gateway with SSO. This does however introduce a weakness in the chain.

I am also interested in using SSH certificates on iOS, but that has turned out to be unsupported in all apps I have tested so far. It is however on the roadmap of Termius, hopefully in the near-future. Follow updates on this subject on my Honk thread about it [4].

For a smaller infrastructure like mine, I have found the manual approach to be sufficient so far.

[1] Scalable and secure access with SSH:
[2] Using a CA with SSH:
[3] Using PIV for SSH through PKCS #11:

Tags: #architecture #ssh #proxyjump #smartcard #pkcs11 #ssh-ca #certificates #signing
Read with Gemini

This blog is powered by cl-yag and Tufte CSS!