SmartCardKerberos

From DcSharedWiki
Jump to: navigation, search

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}