Home
Got Linux ?

Blah blah blah... Mostly technical thoughts, rants and gibberish


Linux smart cards (OpenSC) - How-to

[UPDATED 2022.07.06]

I already covered how baffling smart cards hardware and standards can be:

Linux and smart cards for PKI - Overview

But now that you - and presumably I - are no longer in a state of utter helplessness when confronted by smart cards mumbo jumbo, let’s see what clever things we can do with them. Coming to mind:

Now, each of those buzzwords may be familiar to the Linux layman. But - oh dear! - what intricacies, pitfalls, frustrations and sense of despair they entice when smart cards come into play!

Here be dragons!

Some background

For the sake of the examples below, we’ll assume the following:

And for the sake of completeness, we’ll use:

Software versions

Although all necessary packages are readily available in Debian/Stretch, our chosen use case and hardware requires that we use packages from Debian/Buster:

PKCS#15

The first step is to initalize the smart card and create the necessary PKCS#15 structure, along the PIN codes - aka. tokens/slots - for our declared purposes.

Key pairs and (X.509) certificates

Next, we need to generate/import the required PKI material in each token/slot.

Smart card preparation finalization

Now that we’re done with the smart card itself, we can “finalize” its preparation:

pkcs15-init --finalize

The exact meaning of this step depends on the actual smart card vendor.

PKCS#11

Using smart cards for our purposes will in all cases be achieved thanks to the PKCS#11 software API/stack.

WARNINGS:

PKCS#11 URI

Some libraries and utilities may require you to provide so-called PKCS#11 URI as private key or certificate parameters. Those URIs are standardized by the RFC 7512 and look like:

# WARNING: labels MUST be URL-encoded
# ... private key
PKCS11_RFC7512_URI='pkcs11:type=private;token=Login%20%28MyEID%29;object=username%40example.org'
# ... certificate
PKCS11_RFC7512_URI='pkcs11:type=cert;token=Login%20%28MyEID%29;object=username%40example.org'

LibNSS (Thunderbird/Firefox/Chromium)

PKCS#11 support is implemented in Mozilla Thunderbird, Firefox and Google Chromium thanks to the libnss3 Network Security Services (NSS) library:

NOTE: For Thunderbird and Firefox, you may need to specify the path to your profile instead of the home directory ~/.pki/nssdb location. Look for a pkcs11.txt file, where modutil will actually have added its magic.

WARNING: For Thunderbird, make sure the emailAddress attribute is included in the certificate DN (as opposed to the RFC 3850, ยง.3 recommendation) or it will complain about not being able to find the certificate at the moment the message is sent (albeit being correctly configured in the Account Settings > Security section).

OpenSSL

OpenSSL shall be required to generate the Certificate Signing Request (CSR) corresponding to our login key, as well as issuing the CA-signed X.509 certificates for both login and email purposes. Generally speaking, this implies:

OpenSSL being the horrifying piece of software that it is (at least to my simple self), I invite you to discover OpenSSL-Easy, my humble and certainly poor attempt at making one’s OpenSSL life easier:

OpenSSL-Easy

cURL

cURL relies on OpenSSL engine to perform its PKCS#11 magic. Once the engine installed (see above), it is just a matter of using the proper PKCS#11 URIs as private key and certificate arguments:

# Check the PKCS#11 engine integration
OPENSSL_CONF=./openssl.conf curl --engine list
# [output]
Build-time engines:
  pkcs11

# TLS client authentication
curl \
  --key  'pkcs11:type=private;token=Login%20%28MyEID%29;object=username%40example.org' \
  --cert 'pkcs11:type=cert;token=Login%20%28MyEID%29;object=username%40example.org' \
  https://authenticate.example.org

Secure Shell (SSH)

Using the smart card along SSH for RSA-based authentication is straight-forward, except for one BIG gotcha (see the WARNINGS further below).

WARNINGS:

MIT Kerberos V

Using public key material instead of passwords for Kerberos authentication is known as PKINIT:

MIT Kerberos V PKINIT

Setting it up is rather straight-forward, albeit not devoid of gotchas (see the WARNINGS further below).

WARNINGS:

Pluggable Authentication Module (PAM)

Using the smart card for authenticating on a Linux box implies adding the PKCS#11 module - pam_pkcs11 - to the PAM stack. Again, nothing particularly complicated, except a few gotchas (see the WARNINGS further below).

WARNINGS:

Apache (client) authentication

Using the smart card for (client) authentication on the Apache web server in a LDAP-centric environment is far - very far! - from trivial.

I invite you to read the article dedicated on the subject:

Apache SSL client authentication vs LDAP authorization

OpenVPN

[UPDATED 2021.10.01] Issues reported below are solved as per Debian 11 (Bullseye)

Naively, one would think that using the smart card with OpenVPN would be as simple as:

Poor you! Nope! Not that easy! Well… Yes… That easy, provided you take into account the following well-(and-longime)-known bugs:

Shortly put, you will just need to:

GNU Privacy Guard (GnuPG)

GnuPG is expected to be natively used along ad-hoc OpenPGP cards, totally different beasts from the PKCS#15 and X.509-oriented smart cards we’re now growing accustomed to.

Howevever, GnuPG can be made to work with those, again via the PKCS#11 interface and some additional trickery:

We can therefrom coerce GnuPG into being more X.509 (and S/MIME) friendly:

(TODO: unravel the misteries of using the smart card with gpg itself)

WARNINGS:

Python requests library

Python requests is nowadays the most ubiquitous library for doing HTTP(S).

However, PKCS#11 isn’t natively supported and requires leveraging the M2Crypto library to hook the necessary gear work in, by replacing the https:// adapter (handler) by the M2HttpsAdapter found in:

m2requests.py

Which is as simple as it gets:

from requests import Session

from m2requests import M2HttpsAdapter

request = Session()
request.mount("https://", M2HttpsAdapter())
# PKCS#11 URI; REF: https://tools.ietf.org/html/rfc7512
request.cert=("pkcs11:type=cert;...", "pkcs11:type=private;...")
request.get("https://...")

Encrypt/Decrypt data

Should you need to encrypt/decrypt data using your SmartCard:

# Retrieve the SmartCard's public key
pkcs11-tool -r --id 01 --type cert \
| openssl x509 -inform DER -noout -pubkey \
> pub.pem

# Encrypt the data
cat data \
| openssl rsautl -encrypt -pubin -inkey pub.pem \
| base64 \
| tee data.enc.b64

# Decrypt the data
pkcs11-tool -m RSA-PKCS --decrypt --id 01 -i <(base64 -d data.enc.b64)

Debugging

If you run into troubles, you can easily debug PKCS#11 interactions thanks to OpenSC’s OPENSC_DEBUG or PKCS11SPY environment variables:

Using (and debugging) OpenSC

Shortcoming: Probabilistic Signature Scheme (PSS) not supported

[UPDATED 2021.10.01] This issue is solved as per Debian 11 (Bullseye)

Probabilistic Signature Scheme (PSS), now creeps in TLS connections relying on OpenSSL 1.1.1 or above. This may prevent you to use your credentials on websites that moved on to TLS 1.3 or with OpenVPN (which stubbornly refuses to stick to TLS 1.2 and not use PSS), unless you use:

OpenSC 0.20.0 or above

LibP11 0.4.8 or above (<-> OpenSSL engine)

PKCS#11 Helper patch (forecoming 1.26.0 release <-> OpenVPN)

What about other vendors ?

[UPDATED 2022.07.06; Thanks to Salvatore Stanzione for the heads up]

Keen eyes might have spotted the entirety of this gibberish revolves around OpenSC and its “native” libraries (opensc-pkcs11.so and siblings).

One shall bear in mind some SmartCards might actually not be (entirely/properly) supported by OpenSC and might require their own, vendor/product-specific PKCS#11 library (to be used in place of the opensc-pkcs11.so one in above examples).

Smart card?!? Easy!!!