The OpenPGP card backend

One main purpose of OpenPGP CA is issuing certifications. To issue certifications, the CA uses a private key. This private key must be accessible to the CA.

The simplest way to handle the CA private key is to store it in the CA database, and use it from there. This is what the “softkey” backend does (it uses a “software-based” key). The softkey backend has the benefit that it is easy to use and doesn’t require additional hardware. However, it exposes the CA’s private key material to the risk of exfiltration1 (or malicious use by an attacker).

The OpenPGP card backend is an alternative to the softkey backend. It uses a hardware device to store the CA’s private key. Such devices are designed to keep private key material safer2. Operations that require the CA key are performed directly on the OpenPGP card device.

The OpenPGP card backend adds some complexity (and requires the purchase of additional hardware). As such, using the OpenPGP card backend is a tradeoff. The backend is particularly aimed at users whose threat model includes exfiltration of the CA private key material.

About OpenPGP cards

OpenPGP cards are a type of hardware security device. They follow a published standard3. Many devices4, from a range of vendors, implement this standard.

OpenPGP card devices are relatively inexpensive (tens of EUR/USD).

Card models and features

Different card models support different sets of cryptographic algorithms. Most modern cards support the algorithms that OpenPGP CA uses5 by default.

There are other optional features that only some card models support. Of these, you should particularly consider if your CA instance should make use of the “touch confirmation” feature.

Touch confirmation

Some cards can be configured to require manual (“touch”) confirmation for each cryptographic operation.

In the context of OpenPGP CA, this means that whenever the CA admin wants to issue a certification, the OpenPGP card device requires manual confirmation (usually given by touching a button on the device).

Requiring touch confirmation has the advantage that even if a remote attacker gains control of the computer that runs the CA, they cannot easily issue malicious certifications (they’d need to trick the CA operator into pressing the confirmation button).

If your organization’s CA requires performing many certification operations (for example: renew certifications for 1000s of users, in bulk), the CA admin would need to perform a touch confirmation for every single operation. For bigger CAs, this may be prohibitive.

Also, touch confirmation is not appropriate for use cases in which a CA needs to issue certifications automatically, on the fly, without manual intervention (for example when running a web-portal, where users can upload their public keys and instantly receive certifications).

Custom use of key slots by OpenPGP CA

An OpenPGP key usually consists of multiple cryptographic (sub)keys, internally. The different subkeys are used for different types of operations.

OpenPGP CA keys usually consist of two such cryptographic (sub)keys:

  • A “primary” key, which is used to issue certifications.
  • A signing-capable subkey (which is used when exporting the CA database in the “Keylist” format).

OpenPGP cards have three slots for individual cryptographic keys. Those slots are labeled SIG, DEC and AUT (short for signing, decryption and authentication).

OpenPGP CA uses the “AUT” slot for the certification capable primary key and the “SIG” slot for the signing-capable subkey.

(This, incidentally, means that when used with OpenPGP CA, the fingerprint shown in a card’s “AUT” slot is the primary fingerprint of your CA’s key.)

Cardholder Name

To make cards that are used for a CA easily recognizable, OpenPGP CA sets the Cardholder Name field on the card to the string “OpenPGP CA”6.

Interaction with GnuPG’s scdaemon

If you’re running GnuPG (and its Smartcard daemon scdaemon), you might see unexpected errors that cards are not connected to your system, like this:

$ oca -d example.oca ca init --domain example.org card
Error: No blank OpenPGP card found

If so, this is probably caused by scdaemon’s default behavior of permanently opening cards with exclusive access rights.

To work around this, you can configure GnuPG’s scdaemon subsystem to open cards in shared mode by adding a line pcsc-shared to the file ~/.gnupg/scdaemon.conf7.

Running a CA with the OpenPGP card backend

Initializing a card-backed CA instance

Initializing a new card-backed OpenPGP CA instance requires a blank OpenPGP card (a new card will work, or any card after a factory reset).

The card must support RSA 4096 (this is currently the default algorithm for the CA’s key). Most modern cards support RSA 4096.

To specify which card to use with OpenPGP CA, you can provide the card’s “ident”8 as a parameter. However, usually you can omit this parameter: as long as there is exactly one blank card connected to the system, OpenPGP CA will automatically select that one.

Let’s say you’ve plugged in the blank card FFFE:01234567. Then you can initialize a new CA instance using that card, without explicitly specifying its identity, like this:

$ oca -d example.oca ca init --domain example.org card
Initializing OpenPGP CA on card FFFE:43194240.

Generated new CA key:

-----BEGIN PGP PRIVATE KEY BLOCK-----
Comment: 4E42 D830 8863 526A 1B6F  63A9 7B3D E60F CC7C 2B78
Comment: OpenPGP CA <openpgp-ca@example.org>

xcZYBGPNo8IBEACyqBmZ6r81T5D3HE6Md/i+l+n4YffRPiQYzKnEFPYMDV8dllDE
BwPRkrIVW1LlVj+4XtalKG/Uhr706nw2Bp6RD43zvIdfCUuSZjym6B5rC5XhEcoi
[..]
4pghvR5XTisnuVuy5DHR5NUy6JAT8ioujItSK8ts2pM0GyEhDknNgDQMbXwgAJB1
aCY=
=7BSJ
-----END PGP PRIVATE KEY BLOCK-----

Initialized OpenPGP CA instance:

    CA Domain: example.org
  Fingerprint: 4E42D8308863526A1B6F63A97B3DE60FCC7C2B78
Creation time: 2023-01-22 20:59:46 UTC
   CA Backend: OpenPGP card FFFE:01234567 [User PIN 30186864]

By default, the new CA key gets generated on the host computer, and uploaded to the card.

Note that the output shows that your new CA instance uses the “OpenPGP card” backend. It also shows the card ident and a newly set PIN for the card.

Explicitly selecting a card

Even though it is usually not necessary, you can always explicitly specify the identifier of a card when initializing a CA.

If multiple blank cards are connected to your system, and you don’t explicitly select one, ‘ca init’ will return an error like this:

$ oca -d /tmp/example.oca ca init --domain example.org card
Error: Multiple blank OpenPGP cards found: FFFE:11111111 FFFE:22222222

In this case, you can initialize a CA by explicitly specifying one of these cards, like this:

$ oca -d /tmp/example.oca ca init --domain example.org card FFFE:11111111
[..]

Card PIN

During initialization, OpenPGP CA sets both the User and Admin PIN of your card to a random 8 digit value (both PINs are set to the same value). This new PIN is stored in the CA database (you can inspect it in the output of the ca show command).

This PIN protects you against attackers that have physical access to your OpenPGP card, but no access to the CA database (which contains the PIN):

If someone gets access to your OpenPGP card, and tries to use it on their own computer, they can’t use your CA’s key, as long as they don’t know the PIN (cards only allow 3 attempts at providing the PIN, so a brute-force attack on the PIN is not possible).

During normal operation, the CA will automatically provide the User PIN (as stored in the CA database) to the card, whenever it is needed. Thus, you never need to enter the card PIN while using OpenPGP CA9.

Changing the Admin PIN

You can optionally change the Admin PIN of your card to a different value.

After initialization, OpenPGP CA only accesses the card with user privileges (to perform cryptographic operations). No access with admin privileges is required by OpenPGP CA.

Whether changing the Admin PIN has any benefits depends on details of your setup10. If you want to change it, opgpcard is one tool you can use to do so.

Safekeeping the CA private key

With a softkey-backed CA instance, the private CA key is stored in the database.

By contrast, with card-backed CA instances, the CA system doesn’t have direct access to the CA private key material. The private key material is stored on the card, not in the CA database.

When initializing a card-backed CA, the CA private key is printed to stdout, but not stored.

You will want to keep a copy of this private key for potential future use! For example, you could copy the private key from the terminal into a text file on a USB storage device (an encrypted USB storage device is good practice).

The goal is that you can access the private key material, in the future, but until then keep the key out of reach from potential attackers.

If you don’t make a copy of the private key material (as printed after CA initialization), there is no way you can obtain a copy of your CA private key in the future. You are limited to using the key material via the OpenPGP card. If that card is lost (or breaks), you will need to generate a new CA key, and all users of your CA will need to manually confirm that they want to rely on that new CA key.

Initialization on an air-gapped system

You may want to perform the CA initialization step on an air-gapped computer (possibly running a system like Tails).

When you perform the initialization on an air-gapped system, the CA private key is only handled on that system, and copied to an offline medium (such as encrypted USB storage) from there.

The goal is that no trace of the CA private key material touches any storage that an attacker might gain access to (to prevent any possibility of exfiltration).

After initialization, you can copy the new CA database to the system that you want to run your CA from. A card-backed CA’s database does not contain the CA private key material.

Using the OpenPGP card backend protects the CA private key against exfiltration

The main purpose of using the OpenPGP card backend is that the computer that runs your CA has no direct access to the CA’s private key material.

Using the card backend (after following safe procedures when initializing your CA, and keeping the backup of the CA key safe, as described above), makes exfiltration of the CA key exceedingly unlikely.

However, note that there is still the adjacent risk that an attacker who gains control of the computer that runs your CA could (possibly secretly) use the CA key, on the card, e.g. to issue malicious certifications11.

How to use touch confirmation

If you’re running a CA instance that is operated interactively by the CA admin (that is: every certification is generated by the admin, explicitly), then enabling touch confirmation on your OpenPGP card might be useful (see the discussion about the touch confirmation feature, above).

Configuring the touch confirmation setting

Configuring touch confirmation is possible with some vendor-specific tools, or with the opgpcard tool.

Let’s say we have just initialized an OpenPGP CA instance on a card that supports touch confirmation:

$ oca -dexample.oca ca show
    CA Domain: example.org
  Fingerprint: BF7D78A8BBFACB690F28220DBFB857FE24A06CCE
Creation time: 2023-01-23 13:21:31 UTC
   CA Backend: OpenPGP card 0006:01234567 [User PIN 38343522]

We can inspect the card’s status with opgpcard like this (the -v parameter is required to show the touch policy configuration):

$ opgpcard status -v
OpenPGP card 0006:01234567 (card version 3.4)

Cardholder: OpenPGP CA

Signature key:
  Fingerprint: 2928 3F29 3D75 0039 DD39  F87F 7688 3780 C658 5491
  Creation Time: 2023-01-23 13:21:31 UTC
  Algorithm: RSA 4096 [e 17]
  Touch policy: Off (features: Button)
  Key Status: imported
  User PIN presentation is valid for unlimited signatures
  Signatures made: 0

Decryption key:
  Algorithm: RSA 2048 [e 17]
  Touch policy: Off (features: Button)
  Key Status: not present

Authentication key:
  Fingerprint: BF7D 78A8 BBFA CB69 0F28  220D BFB8 57FE 24A0 6CCE
  Creation Time: 2023-01-23 13:21:31 UTC
  Algorithm: RSA 4096 [e 17]
  Touch policy: Off (features: Button)
  Key Status: imported

Attestation key:
  Algorithm: RSA 2048 [e 17]
  Touch policy: Fixed (features: Button)

Remaining PIN attempts: User: 3, Admin: 3, Reset Code: 0
Key status (#129): imported

Note that the “Touch policy” for the Signature and Authentication keys is set to “Off”.

We can change these settings to a different value, such as “On” or “Fixed” (note that the Admin PIN has been set to 38343522 during CA initialization, as shown above in the output for ca show, so that is the PIN we need to present to change the configuration of the card):

$ opgpcard admin --card 0006:01234567 touch --key SIG --policy Fixed
Enter Admin PIN:
$ opgpcard admin --card 0006:01234567 touch --key AUT --policy Fixed
Enter Admin PIN:

Please check the documentation of your card model to learn which touch confirmation settings it supports. If the “Fixed” setting is available, and if you’re sure that you will always use your CA key interactively, this setting is a good defensive choice12.

Be aware that if you configure your card’s touch confirmation setting to “Fixed”, you can’t change the setting anymore, except with a factory reset of the card (this is the point of the “Fixed” setting). So make sure to consider the trade-offs of touch confirmation before setting your card to this mode (see above, in the “Touch confirmation” section).

Touch confirmation while operating a CA

After configuring touch confirmation to be required for the “AUT” slot, when you run an OpenPGP CA command that issues a certification, the operation blocks - and you see a message that tells you to confirm the operation:

$ oca -d example.oca user add --email alice@example.org
Touch confirmation needed for certification

Once you give the confirmation, the operation completes. If you don’t confirm the operation, the operation times out (and fails) after a while.

If your command certifies multiple identities, you see the confirmation message multiple times and need to confirm repeatedly (once for each operation).

Migrating a softkey instance to the card-backend

If you have been operating a softkey-based OpenPGP CA instance (let’s - again - say in the CA database file example.oca), and you want to continue operating this CA, but change the backend to an OpenPGP card, you can easily migrate your instance as follows.

First: Backup the CA private key

Before you perform the migration, make sure that you have a backup of your CA’s private key! You can either export the CA private key as an armored text file:

$ oca -d example.oca ca private > ca.priv

Or you can store a copy of the full CA database file (pre-migration) in a safe location.

(You will want to store this CA key backup in a safe place. For example: offline, encrypted USB storage.)

Then: Migrate the CA onto an OpenPGP card

Now that a copy of the CA private key material is safely stashed away, let’s migrate our (softkey) CA to the (blank, or factory reset) OpenPGP card FFFE:01234567:

$ oca -d example.oca ca migrate card
Migrating OpenPGP CA instance to card FFFE:01234567.

Caution: After migration is performed, the CA private key material will not
be available in the CA database anymore!

Make sure you have a backup of your CA key before continuing!

Are you sure? (type 'yes' to continue)
yes

Migrated OpenPGP CA instance:

    CA Domain: example.org
  Fingerprint: F149D16EEB76F03059B7280DCA237D22A365F63B
Creation time: 2023-01-23 22:55:50 UTC
   CA Backend: OpenPGP card FFFE:01234567 [User PIN 62013356]

With that, your CA instance is now backed by the OpenPGP card FFFE:01234567. At this point, the CA database does not contain the CA’s private key material anymore13.

Your CA’s private key material has been imported into the AUT and SIG slots of your OpenPGP card. Cryptographic operations using the CA key will from now on be performed on the card.

Like above, the User and Admin PIN of the card have been set to a new 8-digit random value.

Remove CA private key material from the system

After migrating, the system that runs your card-backed CA should ideally not contain copies of the private CA key anymore (after all, the point of running a card-backed CA is to prevent attackers from gaining access to the private CA key).

Therefore, all artifacts that contain the private CA key should be removed from this system. This includes:

  • copies of the CA database while it was soft-key backed, and
  • files containing the exported private CA key.

(You may also want to consider that even after deleting files, copies of the private CA key material may remain on the underlying storage - and may be obtainable by attackers with sufficient access. However, a detailed discussion of this point is out of scope for this text.)

Using a secondary OpenPGP card as backup

If you run a card-backed OpenPGP CA instance, you might want to prepare for loss or breakage of the card that contains your CA’s key. Or, similarly, you may want to set up a second card after the first one has become unavailable.

Let’s say you’re already running a CA, using the database file example.org, on the card FFFE:11111111 (see section “Initializing a card-backed CA instance” for details on initializing a card-backed CA), and you have the CA private key available in the file ca.priv.

Let’s inspect this CA:

$ oca -d example.oca ca show
    CA Domain: example.org
  Fingerprint: 23AC66B369EE135149C8BB1DCF53BCF0714B598A
Creation time: 2023-01-31 18:40:02 UTC
   CA Backend: OpenPGP card FFFE:11111111 [User PIN 33991107]

Importing the key material onto a secondary card

Given the CA private key in a file, you can easily import the key onto a second card.

As discussed above (in the section “Initialization on an air-gapped system”) depending on your security requirements, you might want to only handle the CA private key material on an air-gapped machine, possibly running a system like Tails.

Here, we’re using an example card ident FFFE:22222222 for the second card. As long as there is exactly one blank card connected to your system, OpenPGP CA will automatically select it:

$ oca -d tmp.oca ca init --domain example.org card --import ca.priv
Initializing OpenPGP CA from existing key, on card FFFE:22222222.

Initialized OpenPGP CA instance:

    CA Domain: example.org
  Fingerprint: 23AC66B369EE135149C8BB1DCF53BCF0714B598A
Creation time: 2023-01-31 18:40:02 UTC
   CA Backend: OpenPGP card FFFE:22222222 [User PIN 86998755]

This command imports the key material from ca.priv onto the blank card FFFE:22222222.

As a side effect, this command sets up a new OpenPGP CA instance (in the database file tmp.oca). There’s no strong reason to keep that database file around, but you can. Either way, you should make very sure to keep the PIN for your secondary CA card available (in the example above, the PIN is set to 86998755). Without that PIN, you can not use that OpenPGP card.

Both cards (FFFE:11111111 and FFFE:22222222) now contain the same CA key material and can be used interchangeably. However, note that - as OpenPGP CA sets a random PIN for each card during initialization - the PINs differ between the two cards.

You can supply the identifier for your secondary card, if you like (or if necessary):

$ oca -d tmp.oca ca init --domain example.org card FFFE:22222222 --import ca.priv
[..]

Switching a CA instance to using a different card

Now let’s imagine we lost access to the card FFFE:11111111 and want to switch the operation of our example.oca CA to the card FFFE:22222222.

After plugging in the card FFFE:22222222, we can switch the CA to using it, like this (again, you can explicitly specify the card ident, but you don’t need to, as long as exactly one card is connected to the system that matches your CA’s key):

$ oca -d tmp.oca ca set-backend card

Enter User PIN for OpenPGP card FFFE:22222222:

CA backend configuration is changed.

Note that User PIN entry for the new card is required. Recall that the PIN for the card FFFE:22222222 was set 86998755, above.

Inspecting the CA database example.oca now shows the changed backend configuration (including the PIN):

$ oca -d example.oca ca show
    CA Domain: example.org
  Fingerprint: 23AC66B369EE135149C8BB1DCF53BCF0714B598A
Creation time: 2023-01-31 18:40:02 UTC
   CA Backend: OpenPGP card FFFE:22222222 [User PIN 86998755]

Initializing a CA with on-card key generation

In some contexts, a user may prefer to run a CA with key material that was generated on the OpenPGP card device. This approach has the disadvantage that it is - by definition - not possible to make a backup of the CA private key material. If the card is lost or breaks, the CA key is not available anymore.

Also, when generating key material on the card, you are relying on the card to generate a strong key 14.

If these tradeoffs are acceptable, generating the key material on the card is an easy way to create a CA key that has never been accessible to the computer your CA runs on.

To generate the CA key on your card, add the parameter --generate-on-card (as usual, you can specify the card ident, but you don’t have to, as long as exactly one blank card is connected to the system):

$ oca -d example.oca ca init --domain example.org card --generate-on-card
Generate new OpenPGP CA key on card 0006:01234567.

Note:
1) The private CA key will only exist on the card (you can't make a backup)
2) The randomness your OpenPGP card generates could be worse than your host computer's

Are you sure? (type 'yes' to continue)
yes

Generating RSA4k key material on the card, this might take a while.

[..]

Initialized OpenPGP CA instance:

    CA Domain: example.org
  Fingerprint: 82A6B8E3CA9FC5597DE4C9E2469CC9C649F96106
Creation time: 2023-01-23 16:13:39 UTC
   CA Backend: OpenPGP card 0006:01234567 [User PIN 96659737]

As the output warns, key generation on a card takes some time. For the RSA 4096 keys that OpenPGP CA generated, you should expect key generation on the card to take a minute or two.

Summary

  • OpenPGP card devices are cheap and easy to source.
  • Using the OpenPGP card backend protects the CA key from exfiltration.
  • However, without requiring touch confirmation, there is still a risk that an attacker may secretly issue additional certifications with the CA key.
  • Putting aside touch confirmation: once initialized, operation of a card-backed OpenPGP CA instance works exactly like a softkey-backed CA instance.

  1. An attacker with access to the CA could copy the private CA key to their own computer, and use it to certify keys they control. ↩︎

  2. A softkey can be copied, whereas an hardware device, like an OpenPGP card, has to be physically stolen (which is more noticeable). Alternatively, the CA key could be used by an attacker who has gained control of the system the CA runs on, while the card is connected (also see the discussion of “Touch Confirmation”). ↩︎

  3. The OpenPGP card standard aims to be patent-free and is designed for free usage under the GPL. ↩︎

  4. Vendors that build devices with OpenPGP card support include Nitrokey and YubiKey.

    The Gnuk project offers a Free Software implementation of the standard that can be run on different hardware, including the FST-01SZ and the Nitrokey Start.

    The SmartPGP project offers a JavaCard implementation of the standard, it is also published as Free Software. ↩︎

  5. By default, OpenPGP CA uses an RSA 4096 key. Most current OpenPGP card models support this algorithm. ↩︎

  6. As a precaution, OpenPGP CA won’t attempt to use cards that are set to a different cardholder name. ↩︎

  7. However, be aware that GnuPG’s documentation warns that “Scdaemon assumes exclusive access to the card and for example caches certain information from the card”.

    If you plan to access a card via both GnuPG and different software (e.g. OpenPGP CA), you might want to strategically restart scdaemon after modifying a card’s state from outside GnuPG. ↩︎

  8. Technically speaking, the “ident” is a short identifier for an OpenPGP card that consists of the manufacturer id and the card serial number, separated by a colon.

    One way to learn this identifier of your OpenPGP card is by running the command opgpcard list, while the card is plugged in (the opgpcard command is supplied by the openpgp-card-tools package). ↩︎

  9. In the future, pin-pad entry of the PIN for the CA card will be supported. In that mode of operation, the CA system will not have access to the card PIN. Instead, the operator is required to enter the PIN on the pin-pad of an external card-reader for each cryptographic operation. This provides an additional layer of defense, but at some cost to convenience. ↩︎

  10. Changing the Admin PIN to a value that is not known to the computer that runs the CA is useful if your setup can be undermined with the Admin PIN.

    E.g.: if you rely on a “touch confirmation” requirement, and the configuration for touch requirement is set to “On”, then knowledge of the Admin PIN allows an attacker to temporarily disable touch confirmation. This might be a problem.

    In such cases, you don’t want the computer that runs the CA to have access to the Admin PIN.

    On the other hand, if touch confirmation is either disabled, or configured as permanently required (“Fixed”), then access to the Admin PIN doesn’t have a practical impact (a “Fixed” touch requirement cannot be lifted, short of factory resetting the card, which destroys the key material).

    Aside from the touch confirmation configuration, the Admin PIN does not give meaningful security benefits in the context of an OpenPGP CA instance (recall that the User PIN is readily available via the database). ↩︎

  11. As described in the sections about “touch confirmation”, to make this kind of attack harder, some OpenPGP card devices allow a configuration where each cryptographic operation requires manual (“touch”) confirmation.

    Requiring touch confirmation is a good practice for interactively operated CAs. However, for automated CAs, requiring touch-confirmation is not an option. ↩︎

  12. If the computer that runs the CA has no access to the Admin PIN, configuring touch confirmation to “On” offers a comparable defense. See the discussion in the section “Changing the Admin PIN”. ↩︎

  13. The migration procedure runs the SQLite “VACUUM” command at the end, to dispose of any potentially remaining copies of (parts of) the CA private key material.

    If you don’t want to rely on the Sqlite VACUUM command, you can additionally “.dump” the data from your migrated CA database into text format, optionally inspect the dump, and re-import this dump into a fresh database file.

    You should also consider if any other traces of the private key material might remain on your system (e.g. copies of the CA database, including on the block device that stores the CA database). This may be a non-trivial problem, that we can only hint at, here. ↩︎

  14. One known case of weak on-card key generation is documented by Yubico: “Infineon RSA Key Generation Issue” ↩︎