One of the first things that comes to mind with X.509 certificates and associated private keys is system authentication. In case of GNU/Linux distributions (and, well, most nix systems), this is achieved through PAM* (Pluggable Authentication Modules) system.

This chapter will cover the full set-up of such a system using Debian GNU/Linux as a base.

Software Requirements

For the purpose of this chapter it is assumed that a workstation machine has been supplied with pre-installed Debian Squeeze GNU/Linux distribution. During the (network) installation it is assumed that the Graphical desktop environment task has been selected. It is also recommended to install the SSH server task for ease of administration. Of course, most of the instructions here are actually not DE-dependent, so they should work for any other desktop environment you might prefer.

In addition to the stock software that is available, it will also be required to install some additional packages that will allow for communication with the smart-card.

Install the PKCS#11 provider and interface daemon to smart-cards:

? root
$ apt-get install pcscd opensc

Install the pam_pkcs11 module:

? root
$ apt-get install libpam-pkcs11

Hardware Requirements

If smart-cards are to be used, it's necessary to actually supply the smart-card reader together with a smart-card.

Issuing User's Certificate

A standard procedure can be used for issuing the end user certificates. The important part is that during creation of end entity Token should be set to User Generated

Configuring pam_pkcs11

Several steps need to be taken in order to configure the pam_pkcs11. First of all, it's necessary to create the directory from which the module reads its configuration by default. It's also necessary to supply certificates for pam_pkcs11 used to verify the certificate chain when a client certificate gets supplied.

Create the necessary directories with:

? root
$ mkdir -p /etc/pam_pkcs11/certs/

The certificate authority certificates can be obtained from the following page:


The certificates that must be obtained are the Example Root CA and Example Person CA. These certificates should be downloaded in PEM format, and placed as follows (respectively):

# /etc/pam_pkcs11/certs/example_rootca.pem
# /etc/pam_pkcs11/certs/example_personca.pem

Since pam_pkcs11 utilises the OpenSSL library, it's necessary to create the hash-files for those certificates:

? root
$ pkcs11_make_hash_link /etc/pam_pkcs11/certs/

With everything set in place, now it's necessary to create the actual configuration file for pam_pkcs11:

# /etc/pam_pkcs11/pam_pkcs11.conf
pam_pkcs11 {
  # Do not allow empty passwords (PIN codes).
  nullok = false;

  # Disable debugging output.
  debug = false; 

  # Do not use passwords previously obtained by other PAM modules.
  use_first_pass = false;
  try_first_pass = false;
  use_authtok = false;

  # PKCS#11 module definition which should be used.
  use_pkcs11_module = opensc;

  # PKCS#11 module definition for OpenSC provider.
  pkcs11_module opensc {
    module = /usr/lib/;
    description = "OpenSC PKCS#11 module";

    # Use the first slot available from smart-card reader with an available token.
    slot_description = "none";

    # Location where the c_rehash'ed certificates are stored.
    ca_dir = /etc/pam_pkcs11/certs/;

    # Disable support for threads. This is usually a smart idea since
    # many PKCS#11 implementations can have problems with
    # multi-threading.
    support_threads = false;

    # The verification policy to perform on the smart-card. Verify
    # that provided client certificate is issued by proper CA
    # hierarchy (obtaind from the certs directory), and that signing
    # operation with the smart-card corresponds with the public
    # key/certificate read from it.
    cert_policy = ca, signature;

    # Specify the message which will be shown to users when prompting
    # them for PIN code.
    token_type = "Smart card";

  # Mappers are used to correspond the certificate to a particular
  # user. Since the 'uid' informaiton is containd in the Person end
  # entity profile, have pam_pkcs11 use that for mapping the
  # smart-card owners onto local users. They are processed in
  # specified order.
  use_mappers = uid, cn, null;

  # Null mapper used to deny all access if no other mappers have been
  # successful in mapping the certificate to a user.
  mapper null {
        debug = false;
        module = internal ;
        # Always fail.
        default_match = false;
        # On match, return the specified user.
        default_user = nobody ;

  # Mapper used to map the 'uid' field from the certificate to a local
  # user.
  mapper uid {
        debug = false;
        module = internal;
        ignorecase = false;
        mapfile = "none";
        mapfile = "file:///etc/pam_pkcs11/uid_mapping";



Configuring PAM

The final part of configuration includes setting-up the PAM module to actually use the pam_pkcs11 module. This can be done on either a per-service basis, or by enabling it in every service using PAM for authentication purposes.

To enable it for most/all services using PAM, add the following line to top of the configuration file:

# /etc/pam.d/common-auth
# Provision of smart-card authentication is sufficient to prove the identity.
auth       sufficient

The above change will make it sufficient but not required to use smart-card for log-in purposes. If it's desired to force use of pam_pkcs11, the sufficient option should be replaced with required. This would effectively make the system inaccessible with the use of password, so some careful consideration is required. In order to access the machines as root, you'd need to either set-up the sudo utility, or issue a number of certificates with uid field set to root.

It should also be noted that the ssh server will use the pam_pkcs11 module as well, which could lead in remote denial of service if the attacker locks the smart-card on purpose through multiple failures to provide proper PIN code. Therefore it is a good idea to decouple the sshd PAM configuration file from the common-auth by copy/pasting the contents of old common-auth file directly into the sshd PAM configuration file.

Enabling CRL Checks

In addition to the ca and signature checks, the pam_pkcs11 module is also capable of performing verification of certificate validity using the CRL's.

The CRL's can be dynamically downloaded using the CRL distribution point specified in the certificate, they can be read from a predefined directory, or a combination of these two approaches can be made (first attempt to download the CRL, then use the local copy).

If the CRL's are to be read from the local directory, for start it's necessary to extend the configuration file for the pam_pkcs11 with the following lines (this should be added in the pkcs11_module opensc section):

# /etc/pam_pkcs11/pam_pkcs11.conf
    # Directory from which the CRL's should be read from.
    crl_dir = /etc/pam_pkcs11/crls;

Create the specified directory:

? root
$ mkdir /etc/pam_pkcs11/crls

Finally, it's necessary to specify the CRL verification mode. In order to enable only the offline CRL verification, make the following change:

# /etc/pam_pkcs11/pam_pkcs11.conf
---     cert_policy = ca,signature;
+++     cert_policy = ca,crl_offline,signature;

In order to enable only the online CRL verification, make the following change:

# /etc/pam_pkcs11/pam_pkcs11.conf
---     cert_policy = ca,signature;
+++     cert_policy = ca,crl_online,signature;

In order to enable the online CRL verification with fallback to offline CRL check, make the following change:

# /etc/pam_pkcs11/pam_pkcs11.conf
---     cert_policy = ca,signature;
+++     cert_policy = ca,crl_auto,signature;

Finally, you'll want to periodically download the CRL's is using offline or fallback mode. First create the following utility script:

# /usr/local/bin/

/usr/bin/wget -O /etc/pam_pkcs11/crls/rootca.crl
/usr/bin/wget -O /etc/pam_pkcs11/crls/personca.crl
/usr/bin/pkcs11_make_hash_link /etc/pam_pkcs11/crls/


Set-up the file permissions:

? root
$ chown root.root /usr/local/bin/
$ chmod 700 /usr/local/bin/

Now set-up cron job to execute it periodically (we'll have it download the CRL's every hour):

? root
$ echo "0 * * * * /usr/local/bin/" | crontab

Download the initial CRL's for the first time by hand:

? root