# jnsp


Information Security, Software Development and *NIX

Use a smart card for WiFi authentication on Linux

Smart cards can store and generate cryptographic keys in a way that makes it difficult to extract or copy them by malware or adversaries. Thus, smart cards are ideal to store keys for authentication and digital signatures. Why not just use one to authenticate against your home WiFi network?

I just enrolled for a masters programme at Ruhr-University Bochum which issues smart cards to their students and staff. The smart card already holds an authentication certificate and additionally a signature certificate, both use 2048 bit RSA key pairs. Since I use the card all day for various authentication and signature tasks, it occupies my built-in smart card reader slot all the time.

I wanted to use the card to authenticate against my home wifi network with a TLS client certificate issued by my own PKI. So I created a key pair on the card and issued a new TLS client authentication certificate using that key. I then had a valid certificate on the filesystem with the corresponding private key on the smart card. Excellent.

Setting it up: Step-by-step

You might wind up in a situation where you have to use a smart card to connect to a Wifi network. While this should work out-of-the-box on Microsoft Windows, it isn't really straight forward to do so on a Linux machine. Documenation about how to properly configure wpa_supplicant or netctl with smart cards is scarce and almost never fully covers your use case. I will briefly explain the steps required to get your smart card to work with wpa_supplicant and give you a few hints and references to documentation that might help you in your specific use case.

Step 1: Make sure your smart card and your reader work as intended

Make sure your smart card reader is detected by the OS, that pcscd is running and search for new cards with pcsc_scan. If that fails, consult the documentation on smart cards for your distribution.

Step 2: Get to know the id of the key you want to use on the card

You can store multiple key pairs on a smart card, each is stored in a container on the card which is identified by an ID. Conveniently, you can use the ID to specify which key you want to use on the card.

You can simply run pkcs15-tool -k to list all the private keys present on your token.

$ pkcs15-tool -k
----------------------------------------------------------------
..
Private RSA Key [WIFICERT]
      Object Flags   : [0x3], private, modifiable
      Usage          : [0x6], decrypt, sign
      Access Flags   : [0x15], sensitive, neverExtract
      ModLength      : 2048
      Key ref        : 1 (0x1)
      Native         : yes
      Path           : 3f00501550724b23
      Auth ID        : 01
      ID             : 2087b02273594992b404743cc2b62c30
      MD:guid        : 03db637b-bd5d-3417-0a2a-509812b229c6
..

In my case, I want to use the key with the ID 2087b02273594992b404743cc2b62c30 with the object name WIFICERT. Furthermore, you can later specify the serial number of your token, so that you only use the key with ID 2087b02273594992b404743cc2b62c30 on the card with a specific serial number. This is useful if you use multiple smart cards or tokens (e.g. having both a smart card and a YubiKey plugged in). To list the serial number, display the card information with pkcs15-tool --list-info.

$ pkcs15-tool --list-info
----------------------------------------------------------------
PKCS#15 Card [Student Card]:
      Version        : 0
      Serial number  : 02086AE900000000
      Manufacturer ID: cv cryptovision gmbh (c) v1.1n
      Flags          : Login required, PRN generation, EID compliant

Step 3: Configure wpa_supplicant accordingly

Instead of file paths, you can feed the client_cert and private_key options with PKCS#11 URIs to locate a specific key on a smart card or token. Additionally, you have to specify the pkcs11_engine_path and the pkcs11_module_path with PKCS#11 engines and modules that were built against your version of OpenSSL.

Hint: On Arch Linux, you might need the package libp11 so that OpenSSL can find the correct PKCS#11 engine. You can test this with openssl engine pkcs11. If that OpenSSL command fails, try to fix that first. It should return something like (pkcs11) pkcs11 engine when everything is good.

ctrl_interface=/run/wpa_supplicant
ctrl_interface_group=wheel
pkcs11_engine_path=/usr/lib/pkcs11/opensc-pkcs11.so
pkcs11_module_path=/usr/lib/opensc-pkcs11.so
network={
  ssid="Free wireless charging 🔌"
  key_mgmt=WPA-EAP
  proto=WPA2
  eap=TLS
  identity="Your idendity (typically CN)"
  client_cert="/path/to/certificate.pem"
  private_key="pkcs11:object="WIFICERT";serial=02086AE900000000;id=%2087b02273594992b404743cc2b62c30?pin-value=XXXXXX"
  ca_cert="/path/to/ca-certificate.pem"
  domain_match="hostname.of.radius.server.com"
}

Note the private_key line above. Instead of supplying a path to a key file on the filesystem, we provide a PKCS#11 URI to locate the private key on the smart card. You can do a similar thing with client_cert or ca_cert to also load the certificates from the card. The user PIN has to be stored in plain text within the wpa_supplicant configuration file. I could not find a better solution that protects the PIN in responsible way, thus make sure that the access permissions to the configuration file are sufficiently strict.

Step 4: Connect

Running wpa_supplicant directly can be extremly helpful.

# wpa_supplicant -i wlp1s0 -c /etc/wpa_supplicant/smartcard_network.conf

Later on you might want to use something like netctl as a wrapper around wpa_supplicant to handle things like DHCP, DNS or certain hook scripts you like to run when you connect to a network.

$ cat /etc/netctl/wlan-wpa-config
----------------------------------------------------------------
Interface=wlp1s0
Connection=wireless
Security=wpa-config
IP=dhcp
IP6=stateless
WPAConfigFile='/etc/wpa_supplicant/smartcard_network.conf'