Key management of OpenPGP Card October 11, 2023 | 8 min Read

Key management of OpenPGP Card

Key management of OpenPGP Card

Background

As blank smartcards supporting Java Card 3.0.4 become increasingly available, it is becoming popular to use projects like SmartPGP to create homemade OpenPGP Cards to store OpenPGP private keys. However, although GnuPG (GPG), the currently most widely used OpenPGP implementation, does support OpenPGP Cards, special care must be taken when transferring a private key to the card while retaining a copy in file form, otherwise key loss or other security issues may occur. To avoid these pitfalls, we have summarized a workflow with higher fault tolerance. The methods mentioned in this write-up can be applied to a typical crypto custody solution that utilizes FIPS-140-2/3 HSM (Hardware Security Module) with certain tuning.

Keyrings

The default GPG keyring is located at ${HOME}/.gnupg/, but the --homedir option can also be used at runtime to specify another directory. To keep a file copy of private keys and improve fault tolerance, this workflow uses three keyrings in different locations:

  • Vault keyring ${VAULTRING}: Normally stored offline on a fully encrypted removable storage medium, only connected and decrypted when generating new private keys to access its contents.

  • Temporary keyring ${TMPRING}: Created on-demand in tmpfs and destroyed after use.

  • Working keyring ${WORKRING}: The keyring used for daily cryptographic operations, mainly the default ${HOME}/.gnupg/, but can also be created elsewhere as needed.

If you used to use private keys in file form in the working keyring, you can copy the whole working keyring to a fully encrypted removable storage medium, to create your vault keying, and destroy all private key files stored in ${WORKRING}/private-keys-v1.d/.

Generate master key

It is recommended to complete this step on an offline computer using a live system.

Decrypt and mount ${VAULTRING}, and use $ gpg --homedir ${VAULTRING} --expert --full-gen-key to generate a signing-only key as the master key. Choose the key type supported by your OpenPGP Card, then specify the master key’s lifetime. Note: It is never recommended to generate a key with unlimited lifetime, and it should be renewed or updated before expiration. Subkeys (see below) should have shorter lifetimes than the master key, e.g. if the master key has a lifetime of several years, subkeys may have a 1-year lifetime.

Note: When prompted for real name and email address, it is suggested to first enter a fake name made of random characters and a fake email address made of random characters ending in .invalid, such as [email protected] (the invalidity of such addresses is defined in DNS standards, see https://datatracker.ietf.org/doc/html/rfc6761#section-6.4). After generation, use $ gpg --homedir ${VAULTRING} --expert --edit-key <keyid> to edit the key, add the real name and valid email address with the adduid command, then save the changes and return to the shell with the save command.

Run $ gpg --homedir ${VAULTRING} -o ${VAULTRING}/[email protected] --export <keyid> to export a copy of the bare master key and save it to ${VAULTRING} for later use. OpenPGP key and ciphertext files can be inspected with $ gpg --list-packets, which reads from the filename given or standard input.

Generate subkeys

Again run $ gpg --homedir ${VAULTRING} --expert --edit-key <keyid> to edit the key, and add subkeys with the addkey command. If multiple OpenPGP Cards are available, it is recommended to generate separate encryption and signing subkeys, so the master key on the card is only used for renewal and certifying other’s master keys, and does not need to be used day-to-day. If only one OpenPGP Card is available, generate an encryption subkey and use the master key for signing as well.

If X509 certificates with private keys on the smartcard are needed, an authentication subkey can also be added: when generating the subkey, choose a custom purpose type, then disable its signing and encryption capabilities and enable its authentication capability to generate a subkey for authentication.

After generation, save the changes and return to the shell with the save command.

After the subkeys are generated, export a full public key with $ gpg --homedir ${VAULTRING} -o ${VAULTRING}/[email protected] --export <keyid> and a private key with $ gpg --homedir ${VAULTRING} -o ${VAULTRING}/[email protected] --export-secret-keys <keyid> for later use.

Import private key to the smartcard

Before working with another keyring, remember to kill any existing gpg-agent process: $ killall gpg-agent.

Create a subdirectory in some tmpfs as a temporary keyring, then import the previously exported private key: $ gpg --homedir ${TMPRING} --import ${VAULTRING}/[email protected].

If multiple cards are available, import the master key into the signing slot on one card, and the subkeys into the corresponding slots on another card based on their purposes. If there is only one card, import the master key into the signing slot and the subkeys into the remaining slots based on their purposes.

Run $ gpg --homedir ${TMPRING} --expert --edit-key <keyid> to edit the key, then use the keytocard command after selecting the key with the key command to transfer the key onto the card. GPG will now prompt you for the card’s admin PIN to modify the card’s contents. Whether successful or not, do NOT save with the save command, but quit directly with the quit command, refusing to save. Then you can check the private keys on the card by running $ gpg --homedir ${TMPRING} --card-status, and test using the card’s private keys in ${TMPRING}, i.e. $ gpg --homedir ${TMPRING} ....

Note: Once $ gpg --homedir ${TMPRING} --card-status has been run, the corresponding private key in that keyring will be destroyed and replaced with a stub. Therefore, if the import was unsuccessful due to mismatched card parameters, you can delete ${TMPRING}/private-keys-v1.d, kill gpg-agent, adjust the card parameters, reimport the private key into the temporary keyring and try transferring to the smartcard again. Similarly, never insert the smartcard and run --card-status when working with the vault keyring, or the private keys in the vault keyring may be corrupted.

After successful import, destroy the temporary keyring.

Import public key into working keyring

Copy the previously generated bare master key and full public key into your daily working environment, and import the full public key into the working keyring: $ gpg (--homedir ${WORKRING}) --import [email protected]. Then insert the smartcard containing the imported private keys and run $ gpg (--homedir ${WORKRING}) --card-status. Once the corresponding private keys on the card are identified, stubs will be generated, after which you can use the smartcard in the working keyring.

Publish public key

Since the original OpenPGP keyservers allow arbitrary information to be attached, some people attach huge amounts of junk and signatures to other’s public keys to create enormous keys for attacking, and once user identities (name and email address) are published they cannot be revoked, harming privacy. Therefore, keyservers like https://keys.openpgp.org/ have been set up that can strip identity information including user identities from public keys, but even with a patched GPG, an OpenPGP public key completely devoid of user identities cannot be directly imported. This is where the previously generated bare master key comes in handy.

As no private keys in file form are involved, the following can be done in the working environment.

In some tmpfs, create a subdirectory as a temporary keyring, import the bare master key, delete the real user identities with deluid leaving only the fake identity, then upload the bare master key with the fake identity to a keyserver other than keys.openpgp.org, e.g. $ gpg --homedir ${TMPRING} --keyserver hkps://keyserver2.gnupg.org --send-keys <keyid>. Then back in the working keyring (after killing gpg-agent), upload the full public key to keys.openpgp.org: $ gpg (--homedir ${WORKRING}) --keyserver hkps://keys.openpgp.org --send-keys <keyid>.

This way, other users can find and download the bare master key with the fake identity from other keyservers by searching the keyid, and since it does have a (dummy) user identity after all, the bare master key can be imported into other user’s keyrings. The subkeys can then be obtained from keys.openpgp.org with the patched GPG.

If not prepared in advance, a bare master key can only be created by removing the real user identities and all subkeys from the full public key in a temporary keyring.

The full public key should only be given to trusted individuals.

Renew master key

As no private keys in file form are involved, the following can be done in the working environment.

In some tmpfs, create a subdirectory as a temporary keyring, then import the bare master key $ gpg --homedir ${TMPRING} --import [email protected]. After inserting the card containing the primary private key, first run $ gpg --homedir ${TMPRING} --card-status to generate a stub, then edit the key with $ gpg --homedir ${TMPRING} --expert --edit-key <keyid>, use expire after selecting the master key (which is selected by default) to reset its lifetime. GPG will ask for the card’s user PIN to issue the new lifetime signature. After completion, return to the shell with the save command.

After saving, re-export the bare master key and import it into the working keyring to renew the master key’s lifetime there, and optionally into the vault keyring as well.

The lifetime of subkeys can also be renewed by selecting them, but this is not recommended. It is suggested to generate new subkeys in the vault keyring before expiration of the old subkeys, export a full public key, and import it into the working keyring, allowing the old subkeys to expire.

Change master key

If planning to generate a new master key for the same valid user identity, cross-certification can be done: import the old master key in bare master key form and the new master key into the same temporary keyring, then insert the card containing the old master key, edit the new master key, and generate a certification signature with the tsign command. After saving, insert the card containing the new master key and certify the old master key.

Finally, export the cross-certified full new and old master keys and give to trusted individuals (because certification signatures are also identity information and will be stripped by keys.openpgp.org, they are also not suitable for publishing to other servers).