Background

X.509 / SSL certificates

X.509 certificates are used for authentication in TLS and other protocols. It’s an X.509 certificate which lets you trust, when you connect to https://fedoraproject.org/, that you really are talking to the Fedora server and not an imposter.

The certificate is signed by a trusted "certificate authority", and contains a public key. It lets you know that whoever has the corresponding private key is truly the entity that the certificate claims they are (the 'subject' of the certificate).

As well as authentication for servers, certificates are often used to authenticate clients. For example for VPN connections, and also your 'Fedora certificate' used for koji access is an X.509 certificate.

In this guideline, we are only concerned with your certificates, where you have the corresponding private key and they are used to identify yourself.

These certificates are often stored in files, commonly either a PKCS#12 file (e.g. /usr/share/doc/openvpn/sample/sample-keys/client.p12) or a PKCS#7 or PEM file (e.g. ~/.fedora.cert).

It’s important that a private key remain private; if someone can take a copy of it then they can impersonate you — run a server pretending to be yours, log in to your VPN, abuse Fedora’s koji system in your name, etc.

If a key is stored in a file on the file system, it’s common to use a passphrase to protect it. So the attacker would have to find the passphrase as well as copying the file. But if they can get access to your system, that’s not actually much of an additional barrier.

A much more secure option is to keep the key in a separate hardware 'crypto token'. This will never just give up the key; instead it will perform (sign, encrypt, etc.) operations using the key. So you know the key can never really be stolen.

PKCS#11

PKCS#11 is the API standard for cryptographic tokens. It covers hardware crypto devices as mentioned above, and there are also implementations of PKCS#11 "providers" in pure software — for example the NSS (Firefox) certificate store, GNOME keyring, and SoftHSM.

For our purposes, PKCS#11 mostly just provides an object store for keys and certificates — and for keys it can perform crypto operations for you using the key instead of just handing out the private key data willy-nilly.

For security reasons as described above, it’s very useful to be able to import keys into a PKCS#11 token and use them from there — even if it’s just a software token like the one provided by gnome-keyring — instead of just having them stored in a file in a user’s home directory. That’s exactly what you’re doing when you import a certificate into something like Firefox, Thunderbird or Evolution. There are plans to make NetworkManager always behave this way too: https://wiki.gnome.org/Projects/NetworkManager/PKCS11

Problem statement

Although the use of PKCS#11 is very desirable, it can be very hard to use because each application does things differently. There has historically been no consistent way to specify which certificate to use, from which PKCS#11 provider.

With the pesign tool for signing EFI executables, first you need to create a NSS database and use the modutil tool to add your desired PKCS#11 provider to that environment. Then you can reference that token by its description and a certificate by its "nickname", which may not even be unique. For example, -t "PIV_II (PIV Card Holder pin)" -c "Certificate for PIV Authentication". (You can see this in its full horror in bug 1217727.)

Referencing the same certificate with OpenVPN might look something like: --pkcs11-providers=/usr/lib64/pkcs11/opensc-pkcs11.so --pkcs11-id=piv_II/PKCS\x2315\x20emulated/108421384210c3f5/PIV_II\x20\x28PIV\x20Card\x20Holder\x20pin\x29/01

With wpa_supplicant it’s different again, as shown at http://w1.fi/cgit/hostap/plain/wpa_supplicant/examples/openCryptoki.conf

There are two parts to the problem — there’s the question of which PKCS#11 provider module(s) should be loaded, and the question of how to identify the certificate you want to use from it. And there are two parts to the solution…​

Solution

Which provider to load?

In Fedora (as with most other modern Linux and *NIX systems) this question is answered by p11-kit. A Fedora package containing a PKCS#11 provider module, such as the OpenSC one which supports most major hardware smart cards, will automatically drop a config file into the appropriate place and then its module will automatically appear in well-behaved software which is integrated with the platform and uses p11-kit properly.

Applications can either link against libp11-kit and use its functionality for loading and handling PKCS#11 provider libraries, or there is a simpler option: p11-kit-proxy.so is a PKCS#11 provider module in its own right which inspects the system’s configuration and loads the appropriate modules — as the name implies, proxying them as slots of itself.

If you have an application which is already capable of using PKCS#11 but which doesn’t know about p11-kit, it will mostly need a PKCS#11 provider module to be explicitly specified. All you need to do is make it use p11-kit-proxy.so if the user didn’t specify anything else, and it should Do The Right Thing.

How to specify a certificate?

In April 2015, RFC7512 defined a 'PKCS#11 URI' as a standard way to identify such objects.

This form is already accepted by some programs such as the OpenConnect VPN client. The certificate used in the above examples can be simply used as a client authentication certificate by adding the command-line option -c 'pkcs11:manufacturer=piv_II;id=%01'.

Proposal

Client and server applications

  • Packages which use SSL certificates/keys from a file or elsewhere SHOULD also support using certs/keys from PKCS#11 tokens.

  • Where PKCS#11 objects are specified in a textual form which is visible to the user (e.g. on the command line or in a config file), objects SHOULD be specified in the form of a PKCS#11 URI as as described in RFC7512.

  • Packages which can use PKCS#11 tokens SHOULD automatically use the tokens which are present in the system’s p11-kit configuration, rather than needing to have a PKCS#11 provider explicitly specified.

PKCS#11 Providers

  • Packages providing a PKCS#11 module DSO file SHOULD also provide a corresponding module configuration file in the directory specified by pkg-config p11-kit-1 --variable p11_module_configs (currently /usr/share/p11-kit/modules) so that the new module is automatically visible in well-behaved applications.

Packages providing PKCS#11 module DSO file SHOULD also provide a corresponding module configuration file

Rationale

By consistently using p11-kit for token configuration, and PKCS#11 URIs for specifying objects, we make things a lot simpler for Fedora users. If I want to use a certain certificate from my Yubikey, I should be able to use the URI pkcs11:manufacturer=piv_II;id=%01 consistently in all applications within Fedora and expect it to work.

Examples

OpenVPN

To fix OpenVPN, we first fixed it to load p11-kit-proxy.so by default (thus making the correct tokens visible), And then we fixed its "serialisation" format for how it identifies objects. This was actually in the pkcs11-helper library, and is fixed here.

OpenSSL engine_pkcs11

Similar fixes were submitted to engine_pkcs11 (now in F22+) to make it load p11-kit-proxy.so as its default provider if none is specified, and to make it accept the RFC7512 standard object URIs in place of its own legacy format.

wpa_supplicant

Since wpa_supplicant uses engine_pkcs11, much of the work was already done. All that remained was to make it automatically use the PKCS#11 engine when it detected a "filename" starting with ‘pkcs11:’. (sample commit)

Expectations

Obviously, packagers are not always expected to be proficient coders in the language their packages are written in. We don’t necessarily expect packagers to fix software for themselves, although it’s great when they can.

However, it is certainly within the purview of a packager to work with the upstream developers and ensure that the appropriate features are requested and given due consideration.

Most importantly, it is often the case that software can be built with more than one crypto library, and the feature set available will depend on that choice. For example the OpenConnect VPN client will comply with all of the above requirements when built with GnuTLS, but not when built with OpenSSL. This much certainly is within the direct control of the packager.

Likewise, sometimes all that is required is to configure a piece of software to use p11-kit-proxy.so as its PKCS#11 provider module by default. The p11-kit-proxy.so module is a simple PKCS#11 provider which will read the system’s p11-kit configuration and load all the configured modules for itself, providing a kind of 'proxy'. Again, that’s certainly something a packager can do.

With that in mind, what we ask of packagers is this:

  • Check if your package can use SSL client certificates (e.g. ~/.fedora.cert) for authentication. If it can’t, you have nothing to do.

  • If it can use SSL client certificates, check and see if it can use PKCS#11. If not, please file an upstream bug.

  • If the package can support PKCS#11, make sure it is either fully integrated with libp11-kit to load the correct modules, or at least that it can be configured to load p11-kit-proxy.so as its default PKCS#11 provider when the user doesn’t explicitly specify one. If not, please file an upstream bug.

  • If the package can support PKCS#11, make sure it can accept RFC7512 PKCS#11 URIs as the way to specify certificates. If not, again please file an upstream bug.

If there are things which need to be fixed, it’s helpful also to file a bug in Fedora bugzilla and make it block the PKCS#11 Sanity Tracker bug. Do include a reference to the upstream bug.

Help

There’s a lot of confusing terminology here, but really what we’re asking for is quite simple: If your application can use a certificate from a file, then we would also like it to be able to use a certificate from PKCS#11.

Your application should Do The Right Thing for certificates in PKCS#11, without the user having to explicitly specify which PKCS#11 provider to use and without the user having to learn some weird non-standard way of asking for it.

There is a wiki page at https://fedoraproject.org/wiki/PackageMaintainers/PKCS11 which help you test whether your packages meet these guidelines. If in doubt, file a bug as blocking the PKCS#11 tracker bug and ask for assistance.