About SSH and Smart Card support (RHEL 7)

Updated -

How to set up smart cards for authentication in Red Hat Enterprise Linux 6 is described in the article : How to configure smart card authentication with openssh on Red Hat Enterprise Linux .

Prerequisites

Server side

There is no special requirement for the server side in this setup. openssh-server accepts public key authentication in default configuration (PubkeyAuthentication yes option in sshd_config).

Client side

On the client side, it is required to have installed a PKCS#11 library. There is opensc and coolkey in RHEL7 to interact with smart cards. For special cards supported by IBM, there is opencryptoki package (also providing soft token) and softhsm providing software token.

The list of the libraries with is below:

  • /usr/lib64/pkcs11/opensc-pkcs11.so
    • provided by opensc package
    • works with SmartCard-HSM and YubiKey NEO
  • /usr/lib64/pkcs11/libcoolkeypk11.so
    • for Gemalto hardware tokens, CAC and PIV cards
    • provided by coolkey package
  • /usr/lib64/pkcs11/libopencryptoki.so
    • software token provided by opencryptoki-swtok package
    • other tokens by IBM in related opencryptoki-* packages
  • /usr/lib64/pkcs11/libsofthsm2.so
    • software token provided by softhsm package

To use Secure Shell, you need to install openssh-clients. To work with the smart cards, there are several tools available, that will be also useful, but they are not required for the smart card usage itself:

  • p11tool and certtool
    • provided by gnutls-utils package
    • for key generation and certificates signatures.
  • yubico-piv-tool
  • possibly pkcs11-tools
    • provided by package opensc

Hardware

There are many smart cards and cryptographic devices supporting PKCS#11 interface (Cryptoki) and most of them are supported by the opensc project.
This guide was tested with:

  • SmartCard-HSM
  • YubiKey NEO
  • Gemalto Smart Card (Government PIV cards)

Soft tokens

All the tasks can be performed also with software tokens, which provide simpler and faster way of testing. This article was tested with these software tokens:

  • Opencryptoki-swtok
  • SoftHSM

Generating keys and certificates

Most of the cards are read-only and do not need this step. If your card is issued by the third party (a government), you already have pre-generated keys and certificates and you can skip this section.

Yubikey NEO (yubico-piv-tool)

Yubikey is not exactly smart card, but it has a Privilege and Identification Card (PIV ) module providing PKCS#11 interface. There is a tool provided by the Yubico: yubico-piv-tool to prepare the key. It can be done using these commands:

[localhost ~] $ yubico-piv-tool -s 9a -a generate -o public.pem
[localhost ~] $ yubico-piv-tool -a verify-pin -P 123456 -a selfsign-certificate -s 9a \
    -S "/CN=SSH key/" -i public.pem -o cert.pem
[localhost ~] $ yubico-piv-tool -a import-certificate -s 9a -i cert.pem

(based on developers.yubico.com)

Directly using p11tool

The straight way of accessing smart cards is using tool p11tool. This method can be used for all types of tokens. Some are detected automatically, for some you need to specify shared object handling this token using --provider switch (see examples later). We will go through the same steps as with yubico-piv-tool above

List tokens

First, you need to get URI of your token. This can be found using command:

[localhost ~] $ p11tool --list-tokens
[...]
Token 6:
    URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=[...]
    Label: GnuTLS-Test (UserPIN)
    Type: Hardware token
    Manufacturer: 
    Model: PKCS#15 emulated
    Serial: DECC0100271

Generate key pair

Then you will generate a new RSA key pair:

[localhost ~] $ p11tool --generate-rsa  --login \
     "pkcs11:model=PKCS%2315%20emulated;manufacturer=[...]" 

Directly using pkcs11-tool

Creation of new RSA key pair is quite easy using pkcs11-tool. You need only one command to do so.

List tokens

But again, first, you need to find out in which slot is your card and specify library for a provider in module option (example with opencryptoki software token).

[localhost ~] $  pkcs11-tool --list-slots --module=/usr/lib64/pkcs11/libopencryptoki.so
Available slots:
Slot 0 (0x3): Linux
  token label        : OCToken
  token manufacturer : IBM Corp.
  token model        : IBM SoftTok
  token flags        : rng, login required, PIN initialized, token initialized, SO PIN to be changed, other flags=0x40
  hardware version   : 1.0
  firmware version   : 1.0
  serial num         : 123
[localhost ~] $ pkcs11-tool --keypairgen --key-type=rsa:1024 --slot 03
    --module=/usr/lib64/pkcs11/libopencryptoki.so --login --id=03

Software token using softhsm

First of all you need to create configuration and initialize your token. By default they are stored in system directory, but to have them writeable by user, you should move them to safe location in user's home directory in a safe place and initialize the token:

[localhost ~] $ echo "directories.tokendir = ~/tokens/" > ~/.softhsm2.conf
[localhost ~] $ mkdir ~/tokens
[localhost ~] $ export SOFTHSM2_CONF=~/.softhsm2.conf
[localhost ~] $ softhsm2-util --init-token --slot 0 --label "My token 1"

We can list the tokens and generate the key pair In a similar way as with the hardware tokens before:

[localhost ~] $ p11tool --provider=/usr/lib64/pkcs11/libsofthsm2.so --list-tokens
Token 0:
    URL: pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=c23bb5e747eea608;token=My%20token%201
    Label: My token 1
    Type: Generic token
    Manufacturer: SoftHSM project
    Model: SoftHSM v2
    Serial: c23bb5e747eea608
[localhost ~] $ p11tool --provider=/usr/lib64/pkcs11/libsofthsm2.so --generate-rsa --login \
    "pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=c23bb5e747eea608;token=My%20token%201"

Opencryptoki soft token

This soft token has more complicated setup than the previous one and shows some differences from the others. First you need to add your user to group pkcs11 to be able to access tokens: usermod -aG pkcs11 $USER. Also you need to make sure pkcsslotd daemon is running, which you can verify by running systemctl status pkcsslotd.

You will be able to see the token information and slot information using pkcsconf:

[localhost ~] $ /usr/sbin/pkcsconf -t
Token #3 Info:
    Label: test                            
    Manufacturer: IBM Corp.                       
    Model: IBM SoftTok
[...]

Before creating keys, you need to initialize token in the slot and set up Security Officer (SO) pin and User PIN again using pkcsconf, where the $SLOT_ID is the slot number you will gain as the result of the using the previous command (3 for software token). The default SO password is 87654321.

[localhost ~] $ /usr/sbin/pkcsconf -I -c $SLOT_ID
Enter the SO PIN:  87654321
Enter a unique token label: Test
[localhost ~]$ /usr/sbin/pkcsconf -u -c $SLOT_ID
Enter the SO PIN: 
Enter the new user PIN: 
Re-enter the new user PIN: 

Generate RSA key pair

New key can be generated again using p11tool, but you need to explicitly specify provider and specify an output file for your public key:

[localhost ~] $ p11tool --generate-rsa  --login \
     --provider=/usr/lib64/pkcs11/libopencryptoki.so --outfile id_rsa.pub \
    "pkcs11:model=IBM%20SoftTok;manufacturer=IBM%20Corp.;serial=123;token=test" 
[localhost ~] $ cat id_rsa.pub 
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0p8PKdxsAehCkvylYKVt
[...]iQIDAQAB
-----END PUBLIC KEY-----

This command doesn't store public key on the card so without further workaround, you will not be able to get public key using ssh-keygen nor connect to remote server using ssh.

Create and write certificate

OpenSSH is searching for the public keys and certificates, that have the same ID. If both keys are not written to the card at the same time, you can do that later or store the self-signed certificate as Yubikey does it under the hood of their tool. First of all you need to find out the object that contains your private key. It is done using p11tool, then self sign certificate and then finally store it to the smart card:

[localhost ~] $ p11tool --list-all  --login \
    --provider=/usr/lib64/pkcs11/libopencryptoki.so
Enter PIN: 
Object 0:
    URL: pkcs11:model=IBM%20SoftTok;manufacturer=IBM%20Corp.;serial=123;token=test;id=%10%1a%35%55%33%de%86%f5%fc%04%b2%74%a2%7a%23%1d%95%c2%5b%54;object=test;type=private
    Type: Private key
    Label: test
    Flags: CKA_WRAP/UNWRAP; CKA_PRIVATE; 
    ID: 10:1a:35:55:33:de:86:f5:fc:04:b2:74:a2:7a:23:1d:95:c2:5b:54
[...]
[localhost ~] $ certtool --generate-self-signed  --outfile=id_rsa.cert \
    --provider=/usr/lib64/pkcs11/libopencryptoki.so --load-privkey \
    "pkcs11:model=IBM%20SoftTok;manufacturer=IBM%20Corp.;serial=123;token=test;id=%10%1a%35%55%33%de%86%f5%fc%04%b2%74%a2%7a%23%1d%95%c2%5b%54;object=test;type=private"
[localhost ~] $ p11tool --login --write --load-certificate=id_rsa.cert \
    --provider=/usr/lib64/pkcs11/libopencryptoki.so \
    "pkcs11:model=IBM%20SoftTok;manufacturer=IBM%20Corp.;serial=123;token=test"

Unfortunately, this tool does not accept setting the ID during this time so we need to do that now.

Update certificate ID

The certificate ID has to match with a private key. This can't be done with the current version of p11tool so we need to use more complicated pkcs11-tool. First, we need to find out ids of the objects on our smart card and then modify the ID:

[localhost ~] $ pkcs11-tool --list-objects --slot 3 --login \
    --module=/usr/lib64/pkcs11/libopencryptoki.so
Logging in to "test".
Please enter User PIN: 
Private Key Object; RSA 
  label:      test
  ID:         101a355533de86f5fc04b274a27a231d95c25b54
  Usage:      decrypt, sign, unwrap
[...]
Certificate Object, type = X.509 cert
  label:      SSH cert
  ID:         ab2d0e6743341243dd0f0d66e4e4c1d9c9cb5166
[localhost ~] $ pkcs11-tool --slot 3 --id ab2d0e6743341243dd0f0d66e4e4c1d9c9cb5166 --type=cert \
     --set-id 101a355533de86f5fc04b274a27a231d95c25b54 --login \
    --module=/usr/lib64/pkcs11/libopencryptoki.so

From now on, you will be able to list your public key using ssh-keygen and ssh will correctly pair it with your private key.

Configure authentication in SSH

Connecting using public key in SSH requires the presence of the public key on the server to verify clients signatures. If we don't have the public key stored separately in PEM format, we can get it from the card in expected format using ssh-keygen.

Retrieve public key from card

Public key or certificate is available on smart card

You can list public keys on the card directly using ssh-keygen. There is -D switch for a shared library, which handles smart card communication. In the next example I will use opensc library as an example, but it works the same way with opencryptoki, coolkey or softhsm.

[localhost ~] $ ssh-keygen -D /usr/lib64/pkcs11/opensc-pkcs11.so
ssh-rsa AAAAB3NzaC1yc[...]+g4Mb9

Public key is stored separately in file

The public key generated using p11tool is in PEM format (file id_rsa.pub) and needs to be converted the OpenSSH format. This can be easily done again using ssh-keygen, where -i converts keys between various formats:

[localhost ~] $ ssh-keygen -f id_rsa.pub -i -m PKCS8
ssh-rsa AAAAB3NzaC1...hZaJ

Store public key on server

To enable authentication using a smart card on a remote server, you need to transfer the public key (smartcard.pub) retrieved in the previous step (in the OpenSSH format) to the remote server. You can do it by simply copy paste to remote shell, or by using ssh-copy-id:

[localhost ~] $ SSH_COPY_ID_LEGACY=1 ssh-copy-id -i smartcard.pub user@hostname
user@hostname's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh user@hostname"
and check to make sure that only the key(s) you wanted were added.

You will get prompted for your current remote password. Storing a public key without a private key file requires to use the SSH_COPY_ID_LEGACY=1 environment variable (or -f switch in Fedora).

Authenticate to server with smartcard

OpenSSH has a possibility to read public key from a smart card and let it do operations with a private key without exposing the key itself. This means that the private key doesn't leave the card. A smart card is handled by a shared library, which you need to provide to the `ssh command, so the client will know how to communicate with the card. The connection to remote server can be issued using this command (a smart card is usually protected by PIN so you are prompted before accessing the operation on the card):

[localhost ~] $ ssh -I /usr/lib64/pkcs11/opensc-pkcs11.so hostname
Enter PIN for 'Test (UserPIN)': 
[hostname ~] $

Client configuration

Additionally, you can store the path to this library in your ssh_config and save typing the path over and over again for every connection. Create ~/.ssh/config or append this snippet to your existing file

Host hostname
    PKCS11Provider /usr/lib64/pkcs11/opensc-pkcs11.so

Then you can connect simply without any additional options:

[localhost ~] $ ssh hostname
Enter PIN for 'Test (UserPIN)': 
[hostname ~] $

Using ssh-agent

To save the typing of your PIN every time you connect using a smart card, you can add the card into your ssh-agent. If it is not running in your session yet, you need to run it by your own:

eval `ssh-agent`

Adding card is done using ssh-add:

[localhost ~] $ ssh-add -s /usr/lib64/pkcs11/opensc-pkcs11.so
Enter PIN for 'Test (UserPIN)': 
Card added: /usr/lib64/pkcs11/opensc-pkcs11.so

The next connections will not require to enter a PIN and you will not have to specify PKCS11Provider option in configuration nor library path on command line:

[localhost ~] $ ssh hostname
[hostname ~] $

Card can be later removed using this command:

[localhost ~] $ ssh-add -e /usr/lib64/pkcs11/opensc-pkcs11.so
Card removed: /usr/lib64/pkcs11/opensc-pkcs11.so