SmartCardKerberos

From DcSharedWiki
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

How to use smart card with Kerberos.


Requirements

  1. Software: Here programs that will be used. In parenthesises I have mentionned versions I have used.
  2. Hardware
    • a smart card: Feitian PKI card bought on http://www.gooze.eu/ There is a lot of documentation on this website.
    • one smart card reader (mines are Gemalto PC Twin Reader and Gemalto USB Shell Token v2 for SIM card size)

External links

Typographical conventions

  • in commands listed below, you must replace `XX` depending of your configuration
  • Constant width is used for `keywords`

Initialize smart card

An empty smart card can be initialized with:

# pin & puk will be asked
pkcs15-init --create-pkcs15 --profile pkcs15+onepin --use-default-transport-key --label "Pilou" 


A not empty smart card can be erased and initialized with:

pkcs15-init -E --label "Pilou"


Create a PKI

Private keys of root CA, server CA and clients CA will be stored on a smart card.

PKI structure

  • Root CA: sign servers CA and clients CA. Root CA is a self-signed certificate. (directory `./ca`)
  • servers CA: sign servers certificates (directory `./cassl`)
    • A server certificate will be signed by servers CA. For example kdc certificate will be signed by servers CA.
  • clients CA: sign clients certificates (directory `./client`)
    • A client certificate will be signed by client CA. For example user certificate will be signed by clients CA.

`openssl.cnf` with 3 CA:

[ CA_root_default ]
dir              = ./root_ca
certs            = $dir/certs             # Where the issued certs are kept
crl_dir          = $dir/crl               # Where the issued crl are kept
database         = $dir/index.txt         # database index file.
new_certs_dir    = $dir/newcerts          # default place for new certs.
certificate      = $dir/cacert.pem        # The CA certificate
serial           = $dir/serial            # The current serial number
crlnumber        = $dir/crlnumber         # the current crl number
crl              = $dir/crl.pem           # The current CRL
RANDFILE         = $dir/private/.rand     # private random number file
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt         = ca_default             # Subject Name options
cert_opt         = ca_default             # Certificate field options
default_days     = 365                    # how long to certify for
default_crl_days = 30                     # how long before next CRL
default_md       = sha1                   # which md to use.
preserve         = no                     # keep passed DN ordering
policy           = policy_match

[ CA_serveur_default ]
dir              = ./servers_ca
certs            = $dir/certs             # Where the issued certs are kept
crl_dir          = $dir/crl               # Where the issued crl are kept
database         = $dir/index.txt         # database index file.
new_certs_dir    = $dir/newcerts          # default place for new certs.
certificate      = $dir/cassl.pem         # The CA certificate
serial           = $dir/serial            # The current serial number
crlnumber        = $dir/crlnumber         # the current crl number
crl              = $dir/crl.pem           # The current CRL
RANDFILE         = $dir/private/.rand     # private random number file
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt         = ca_default             # Subject Name options
cert_opt         = ca_default             # Certificate field options
default_days     = 365                    # how long to certify for
default_crl_days = 30                     # how long before next CRL
default_md       = sha1                   # which md to use.
preserve         = no                     # keep passed DN ordering
policy           = policy_match

[ CA_client_default ]
dir              = ./clients_ca
certs            = $dir/certs             # Where the issued certs are kept
crl_dir          = $dir/crl               # Where the issued crl are kept
database         = $dir/index.txt         # database index file.
new_certs_dir    = $dir/newcerts          # default place for new certs.
certificate      = $dir/client.pem        # The CA certificate
serial           = $dir/serial            # The current serial number
crlnumber        = $dir/crlnumber         # the current crl number
crl              = $dir/crl.pem           # The current CRL
RANDFILE         = $dir/private/.rand     # private random number file
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt         = ca_default        # Subject Name options
cert_opt         = ca_default        # Certificate field options
default_days     = 365               # how long to certify for
default_crl_days = 30                # how long before next CRL
default_md       = sha1              # which md to use.
preserve         = no                # keep passed DN ordering
policy           = policy_user

[ policy_match ]
countryName            = match
stateOrProvinceName    = match
organizationName       = match
organizationalUnitName = optional
commonName             = supplied
emailAddress           = optional

[ policy_user ]
countryName            = match
stateOrProvinceName    = match
organizationName       = match
organizationalUnitName = optional
commonName             = supplied
emailAddress           = supplied


# create directory
mkdir -p root_ca/newcerts
touch root_ca/index.txt touch root_ca/index.txt root_ca/index.txt.attr
echo "00" > root_ca/serial
mkdir server_ca
mkdir client_ca


Create root CA

Root CA is a self-signed certificate. In order to create the certificate you need to generate a RSA key. This key can be either:

  • generated on the smart card
  • generated on a computer and copied on the smart card

RSA generated on the smart card

# (pin will be asked)
pkcs15-init --generate-key rsa/2048 --auth-id XX


Value of "--auth-id" parameter can be retreived with:

pkcs15-tool --list-pins # use 'ID' field


RSA generated on a computer and copied

cd root_ca
openssl genrsa 2048 > id_rsa.pem
openssl rsa -pubout < id_rsa.pem > id_rsa.pub
pkcs15-init --store-private-key id_rsa.pem --auth-id XX # PIN will be asked or '--pin' can be used


Create certificate

Then the root CA can be created from the RSA key:

openssl req -config openssl.cnf -engine pkcs11 -key slot_XX-id_XXX -keyform engine -extensions CA_ROOT -new -x509 -out root_ca/ca.pem -text
  • `id` number can be found with the following command:
pkcs11-tool --list-keys # check ID field
  • `id` is same as value of parameter `--auth-id` and `slot` number can be found with the following command:
pkcs11-tool --list-slots 
  • `-key slot_XX-id_XXX -keyform engine` indicate that private key XXX is stored on smart card at slot XX
  • Value of parameter `-engine` is a name of an engine defined in `openssl.cnf`:
[openssl_init]
engines     = engine_section

[engine_section]
pkcs11 = pkcs11_section

[pkcs11_section]
engine_id = pkcs11
dynamic_path = /usr/lib/engines/engine_pkcs11.so
MODULE_PATH = /usr/lib/opensc-pkcs11.so
init = 0
  • ` -extensions` refers to certificate extensions to be added to root CA certificate. Extensions are defined in `openssl.cnf`:
[CA_ROOT]
nsComment                       = "Root CA"
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer:always
basicConstraints                = critical,CA:TRUE,pathlen:1
keyUsage                        = keyCertSign, cRLSign


Create server CA

Server CA will be signed by root CA. In order to create the server CA certificate you need to generate a RSA key. Store the private key in smart card. See previous paragraph.

  • Create a certificate request CSR from previously created RSA key:
# -key refers to private key of server CA, pin will be asked
openssl req -config openssl.cnf -engine pkcs11 -key slot_X-id_XX -keyform engine -new -out server_ca/ca.csr
  • Validate certificate signing request with the private key of root CA (`-name` parameter specifies which CA is used when signing the CSR):
# -key refers to private key of root CA, pin will be asked
openssl ca -config openssl.cnf -engine pkcs11 -keyfile slot_X-id_XX -keyform engine -name CA_root_default -extensions CA_SERVER_SSL -out server_sa/ca.pem -infiles server_ca/ca.csr
  • certificate extensions `CA_SERVER_SSL` is:
[CA_SERVER_SSL]
nsComment                       = "server CA SSL"
basicConstraints                = critical,CA:TRUE,pathlen:0
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer:always
issuerAltName                   = issuer:copy
keyUsage                        = keyCertSign, cRLSign
nsCertType                      = sslCA


Create a client CA

Client CA will be signed by root CA. In order to create the client CA certificate you need to generate a RSA key. Store the private key in smart card. See previous paragraph.

  • Create a certificate request CSR from previously created RSA key:
# -key refers to private key of client CA, pin will be asked
openssl req -config openssl.cnf -engine pkcs11 -key slot_X-id_XX -keyform engine -new -out client/client.csr
  • Validate certificate signing request with the private key of root CA:
# -key refers to private key of root CA, pin will be asked
openssl ca -config openssl.cnf -engine pkcs11 -keyfile slot_X-id_XX -keyform engine -name CA_root_default -extensions CA_CLIENT_SSL -out client/client.pem -infiles client/client.csr
  • certificate extensions `CA_CLIENT_SSL` is:
[CA_CLIENT_SSL]
nsComment                       = "CA client SSL"
basicConstraints                = critical,CA:TRUE,pathlen:0
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer:always
issuerAltName                   = issuer:copy
keyUsage                        = keyCertSign, cRLSign
nsCertType                      = sslCA


Create a server certificate for Kerberos KDC

In order to create a KDC certificate you need to generate a RSA key on a computer. `kdc` will need access to the private key, don't store it in a smart card!

openssl genrsa 2048 > ./cassl/id_kdc.pem
openssl rsa -pubout < ./cassl/id_kdc.pem > ./cassl/id_kdc.pub


Create the CSR from the previously created RSA key:

export REALM=MYREALM.ORG
openssl req -config openssl.cnf -key ./cassl/id_kdc.pem -new -out cassl/kdcssl.csr


Validate certificate signing request with the private key of server CA:

export REALM=MYREALM.ORG
# -key refers to private key of server CA, pin will be asked
openssl ca -config openssl.cnf -engine pkcs11 -keyfile slot_X-id_XX -keyform engine -name CA_server_default -extensions KDC_CERT -out cassl/kdcssl.pem -infiles cassl/kdcssl.csr
  • certificate extensions `KDC_CERT` are
[ KDC_CERT ]
nsComment                       = "KDC SERVER SSL"
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer:always
issuerAltName                   = issuer:copy
keyUsage                        = nonRepudiation, digitalSignature, keyEncipherment, keyAgreement
extendedKeyUsage                = pkkdcekuoid
basicConstraints                = critical,CA:FALSE

This OID need to be added to `openssl.cnf`:

[openssl_init]
engines     = engine_section
oid_section = new_oids

[ new_oids ]
pkkdcekuoid=1.3.6.1.5.2.3.5


ND: you can take a look at http://www.oid-info.com/

Create a client certificate

Client certificate will be signed by client CA. In order to create the client certificate you need to generate a RSA key. Store the private key in smart card. See previous paragraph.

  • Create a certificate request CSR from previously created RSA key:
# -key refers to private key of client certificate, pin will be asked
openssl req -config openssl.cnf -engine pkcs11 -key slot_X-id_XX -keyform engine -new -out client/pilou.csr


  • Validate certificate signing request with the private key of client CA:
#  -key refers to private key of client CA, pin will be asked
export REALM=MYREALM.ORG
export LOGIN=pilou
openssl ca -config openssl.cnf -engine pkcs11 -keyfile slot_X-id_XX -keyform engine -name CA_client_default -extensions CLIENT_CERT -out client/pilou.pem -infiles client/pilou.csr
  • `-extensions CLIENT_CERT` refers to certificate extensions to be added to client certificate:
[CLIENT_SSL]
nsComment                       = "Certificat Client SSL"
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer:always
issuerAltName                   = issuer:copy
basicConstraints                = critical,CA:FALSE
keyUsage                        = digitalSignature, nonRepudiation
nsCertType                      = client
  • Store certificate on smart card:
pkcs15-init --store-certificate client/pilou.pem --auth-id XX --id XX --format pem


Kerberos configuration

Client configuration

MIT Kerberos documentation

### file /etc/krb5.conf ###
[realms]
    MYREALM.ORG = {
        ...
        pkinit_anchors = FILE:/home/lilou/pki/ca/cacert.pem
        pkinit_pool = FILE:/home/lilou/pki/cassl/cassl.pem
        pkinit_pool = FILE:/home/lilou/pki/client/client.pem
    }


KDC configuration

MIT Kerberos documentation

###  ###
[realms]
    MYREALM.ORG = {
        ...
        pkinit_identity = FILE:/etc/krb5kdc/kdcssl.pem,/etc/krb5kdc/id_kdc.key
        pkinit_anchors = FILE:/etc/krb5kdc/cacert.pem
        pkinit_pool = FILE:/etc/krb5kdc/cassl.pem
        pkinit_eku_checking = kpClientAuth
    }


OpenSSL configuration

Here is my complete openssl.cnf file:


# This definition stops the following lines choking if HOME isn't
# defined.
HOME            = .
RANDFILE        = $ENV::HOME/.rnd

openssl_conf = openssl_init

[openssl_init]
engines     = engine_section
oid_section = new_oids

[ new_oids ]
pkkdcekuoid=1.3.6.1.5.2.3.5

[ ca ]
default_ca = CA_root_default

####################################################################
[ CA_root_default ]
dir              = ./ca
certs            = $dir/certs             # Where the issued certs are kept
crl_dir          = $dir/crl               # Where the issued crl are kept
database         = $dir/index.txt         # database index file.
new_certs_dir    = $dir/newcerts          # default place for new certs.
certificate      = $dir/cacert.pem        # The CA certificate
serial           = $dir/serial            # The current serial number
crlnumber        = $dir/crlnumber         # the current crl number
crl              = $dir/crl.pem           # The current CRL
RANDFILE         = $dir/private/.rand     # private random number file
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt         = ca_default             # Subject Name options
cert_opt         = ca_default             # Certificate field options
default_days     = 365                    # how long to certify for
default_crl_days = 30                     # how long before next CRL
default_md       = sha1                   # which md to use.
preserve         = no                     # keep passed DN ordering
policy           = policy_match

[ CA_serveur_default ]
dir              = ./cassl
certs            = $dir/certs             # Where the issued certs are kept
crl_dir          = $dir/crl               # Where the issued crl are kept
database         = $dir/index.txt         # database index file.
new_certs_dir    = $dir/newcerts          # default place for new certs.
certificate      = $dir/cassl.pem         # The CA certificate
serial           = $dir/serial            # The current serial number
crlnumber        = $dir/crlnumber         # the current crl number
crl              = $dir/crl.pem           # The current CRL
RANDFILE         = $dir/private/.rand     # private random number file
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt         = ca_default             # Subject Name options
cert_opt         = ca_default             # Certificate field options
default_days     = 365                    # how long to certify for
default_crl_days = 30                     # how long before next CRL
default_md       = sha1                   # which md to use.
preserve         = no                     # keep passed DN ordering
policy           = policy_match

[ CA_client_default ]
dir              = ./client
certs            = $dir/certs             # Where the issued certs are kept
crl_dir          = $dir/crl               # Where the issued crl are kept
database         = $dir/index.txt         # database index file.
new_certs_dir    = $dir/newcerts          # default place for new certs.
certificate      = $dir/client.pem        # The CA certificate
serial           = $dir/serial            # The current serial number
crlnumber        = $dir/crlnumber         # the current crl number
crl              = $dir/crl.pem           # The current CRL
RANDFILE         = $dir/private/.rand     # private random number file
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt         = ca_default        # Subject Name options
cert_opt         = ca_default        # Certificate field options
default_days     = 365               # how long to certify for
default_crl_days = 30                # how long before next CRL
default_md       = sha1              # which md to use.
preserve         = no                # keep passed DN ordering
policy           = policy_user

[ policy_match ]
countryName            = match
stateOrProvinceName    = match
organizationName       = match
organizationalUnitName = optional
commonName             = supplied
emailAddress           = optional

[ policy_user ]
countryName            = match
stateOrProvinceName    = match
organizationName       = match
organizationalUnitName = optional
commonName             = supplied
emailAddress           = supplied

####################################################################
[ req ]
default_bits           = 1024
default_keyfile        = privkey.pem
distinguished_name     = req_distinguished_name

# This sets a mask for permitted string types. There are several options. 
# default: PrintableString, T61String, BMPString.
# pkix     : PrintableString, BMPString.
# utf8only: only UTF8Strings.
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
# so use this option with caution!
string_mask = nombstr

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = FR
countryName_min             = 2
countryName_max             = 2
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = Ile de France
localityName                = Locality Name (eg, city)
localityName_default        = Paris
0.organizationName          = Organization Name (eg, company)
0.organizationName_default  = PilouCorp
organizationalUnitName      = Organizational Unit Name (eg, section)
commonName                  = Common Name (eg, YOUR name)
commonName_max              = 64
emailAddress                = Email Address
emailAddress_max            = 64

[engine_section]
pkcs11 = pkcs11_section

[pkcs11_section]
engine_id = pkcs11
dynamic_path = /usr/lib/engines/engine_pkcs11.so
MODULE_PATH = /usr/lib/opensc-pkcs11.so
init = 0

[CA_ROOT]
nsComment                       = "Root CA"
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer:always
basicConstraints                = critical,CA:TRUE,pathlen:1
keyUsage                        = keyCertSign, cRLSign

[CA_SERVER_SSL]
nsComment                       = "server CA SSL"
basicConstraints                = critical,CA:TRUE,pathlen:0
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer:always
issuerAltName                   = issuer:copy
keyUsage                        = keyCertSign, cRLSign
nsCertType                      = sslCA

[SERVER_SSL]
nsComment                       = "Certificat Serveur SSL"
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer:always
issuerAltName                   = issuer:copy
basicConstraints                = critical,CA:FALSE
keyUsage                        = digitalSignature, nonRepudiation, keyEncipherment
nsCertType                      = server
extendedKeyUsage                = serverAuth

[CA_CLIENT_SSL]
nsComment                       = "CA client SSL"
basicConstraints                = critical,CA:TRUE,pathlen:0
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer:always
issuerAltName                   = issuer:copy
keyUsage                        = keyCertSign, cRLSign
nsCertType                      = sslCA

[CLIENT_SSL]
nsComment                       = "Certificat Client SSL"
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer:always
issuerAltName                   = issuer:copy
basicConstraints                = critical,CA:FALSE
keyUsage                        = digitalSignature, nonRepudiation
nsCertType                      = client

[ KDC_CERT ]
nsComment                       = "KDC SERVEUR SSL"
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer:always
issuerAltName                   = issuer:copy
keyUsage                        = nonRepudiation, digitalSignature, keyEncipherment, keyAgreement
extendedKeyUsage                = pkkdcekuoid
basicConstraints                = critical,CA:FALSE
# Add id-pkinit-san (pkinit subjectAlternativeName)
subjectAltName                  = otherName:1.3.6.1.5.2.2;SEQUENCE:kdc_princ_name

[kdc_princ_name]
realm          = EXP:0,GeneralString:${ENV::REALM}
principal_name = EXP:1,SEQUENCE:kdc_principal_seq

[kdc_principal_seq]
name_type   = EXP:0,INTEGER:1
name_string = EXP:1,SEQUENCE:kdc_principals

[kdc_principals]
princ1 = GeneralString:krbtgt
princ2 = GeneralString:${ENV::REALM}

[ CLIENT_CERT ]
nsComment                       = "KRB CLIENT SSL"
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer:always
issuerAltName                   = issuer:copy
keyUsage                        = digitalSignature, keyEncipherment, keyAgreement
extendedKeyUsage                = 1.3.6.1.5.2.3.4
basicConstraints                = CA:FALSE
subjectAltName                  = otherName:1.3.6.1.5.2.2;SEQUENCE:princ_name

[princ_name]
realm          = EXP:0, GeneralString:${ENV::REALM}
principal_name = EXP:1, SEQUENCE:principal_seq

[principal_seq]
name_type   = EXP:0, INTEGER:1
name_string = EXP:1, SEQUENCE:principals

[principals]
princ1 = GeneralString:${ENV::CLIENT}