Client Certificate PKI Configuration

The idea is simple, the implementation is complex and the execution simple.

The idea is that the client sends a certificate (and key) to the server. The server checks the certificate against a CA and, if it matches, approves the authentication.

The implemention means creating a CA (NOT using a commercial one - this is completely insecure) and a client certificate from the CA. This PKI could be really complicated (Root, Intermediates, signing etc.) or it could be a single Root CA and one certificate per device or user.

The complications are that the certs must contain certain attributes and have certain attribute values formatted in certain ways. Different OSes have different requirements.

The execution is easy. The client sends the cert to the server, which compares it to the stored CA. Again, this could be made more complex by having the CA stored elsewhere and the RADIUS server having to make a call to it.

The approach here is going to be simple. A Root CA with a single certificate and using eapol_test to test.

openssl.conf:

#
# OpenSSL configuration file.
#
# Establish working directory.
dir = .
[ ca ]
default_ca	= CA_default
[ CA_default ]
serial	= $dir/serial
database	= $dir/index.txt
new_certs_dir	= $dir/newcerts
certificate	= $dir/cacert-2021.pem
private_key	= $dir/private/cakey.pem
default_days	= 36526
default_md	= SHA256
preserve	= no
email_in_dn	= no
nameopt	= default_ca
certopt	= default_ca
policy	= policy_match
crlDistributionPoints = URI:http://crldp.govroam.uk/crldp.crl
[ policy_match ]
countryName	= optional
stateOrProvinceName	= optional
localityName	= optional
organizationName	= optional
organizationalUnitName	= optional
commonName	= supplied
emailAddress	= optional
[ req ]
default_bits	= 2048	# Size of keys
default_keyfile	= key.pem	# name of generated keys
string_mask	= default	# permitted characters
distinguished_name	= req_distinguished_name
x509_extensions	= v3_ca
[ req_distinguished_name ]
# Variable name	Prompt string
#----------------------	----------------------------------
countryName	= Country Name (2 letter code)
countryName_min	= 2
countryName_max	= 2
stateOrProvinceName	= State or Province Name (full name)
localityName	= Locality Name (city, district)
0.organizationName	= Organization Name (company)
organizationalUnitName	= Organizational Unit Name (department, division)
emailAddress	= Email Address
emailAddress_max	= 40
commonName	= Common Name (hostname, IP, or your name)
commonName_max	= 64
# Default values for the above, for consistency and less typing.
# Variable name	Value
#------------------------------	------------------------------
countryName_default	= GB
stateOrProvinceName_default	= England
localityName_default	= Manchester
0.organizationName_default	= Scarfolk
organizationalUnitName_default = Scarfolk
emailAddress_default = mike.richardson@jisc.ac.uk
distinguished_name	= req_distinguished_name
req_extensions	= v3_req
[ v3_ca ]
basicConstraints	= CA:TRUE
subjectKeyIdentifier	= hash
authorityKeyIdentifier	= keyid:always,issuer:always
crlDistributionPoints = URI:http://crldp.govroam.uk/crldp.crl
[ v3_req ]
basicConstraints	= CA:FALSE
subjectKeyIdentifier	= hash
[ xpclient_ext ]
extendedKeyUsage = 1.3.6.1.5.5.7.3.2
crlDistributionPoints = URI:http://crldp.govroam.uk/crldp.crl
[ xpserver_ext ]
extendedKeyUsage = 1.3.6.1.5.5.7.3.1
crlDistributionPoints = URI:http://crldp.govroam.uk/crldp.crl
[ server ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
#extendedKeyUsage = serverAuth
extendedKeyUsage = 1.3.6.1.5.5.7.3.1, serverAuth
crlDistributionPoints = URI:http://crldp.govroam.uk/crldp.crl
[ client ]
basicConstraints = CA:FALSE
nsCertType = client, email, server
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth, emailProtection
crlDistributionPoints = URI:http://crldp.govroam.uk/crldp.crl

The defaults need changing, as do the crlDistributionPoints. The config has been adapted to many things over years so is far from optimal. There will be stuff that's unnecessary and stuff left out but it's been tested and I know it works.

xpextensions:

[ xpclient_ext ]
extendedKeyUsage = 1.3.6.1.5.5.7.3.2
[ xpserver_ext ]
extendedKeyUsage = 1.3.6.1.5.5.7.3.1

The Root CA

First, generate the certificate:

openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 36500 -config ./openssl.cnf -sha256

Remember the password, you'll need it later for the client cert.

Then, convert it to a PCKS12 file:

openssl pkcs12 -export -out cacert.pfx -inkey private/cakey.pem -in cacert.pem

Example:

Certificate:
Data:
Version: 3 (0x2)
Serial Number:
1f:87:4b:2f:67:d2:6f:30:81:3a:fd:20:00:4f:03:eb:3d:c4:57:38
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = GB, ST = England, L = Manchester, O = Scarfolk, OU = Scarfolk, emailAddress = mike.richardson@jisc.ac.uk
Validity
Not Before: May 4 13:15:23 2021 GMT
Not After : Apr 10 13:15:23 2121 GMT
Subject: C = GB, ST = England, L = Manchester, O = Scarfolk, OU = Scarfolk, emailAddress = mike.richardson@jisc.ac.uk
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:cb:c3:99:ed:69:cc:ee:3b:0f:e1:2d:a6:f9:94:
33:b7:f6:bb:b7:4a:b7:37:9f:c7:36:f6:24:5c:89:
67:36:67:36:2f:51:c0:c3:34:e3:74:a4:88:68:7f:
03:48:97:f2:79:ba:25:24:66:70:b8:58:38:9b:de:
bf:53:6d:09:81:13:75:0a:75:cd:2f:35:18:e8:7f:
33:a9:81:7f:72:2d:bf:97:1c:9a:5c:95:8e:e5:97:
1c:cb:83:5b:ed:7b:e7:af:da:84:97:22:1b:52:7b:
34:a5:5f:ae:88:94:1e:56:27:74:74:2d:a9:41:bf:
f8:69:8a:73:7b:1d:96:0f:52:cd:6c:d5:fd:d9:ea:
61:5e:a6:b4:49:0a:c0:5f:0f:4e:f4:3c:ad:11:2c:
05:10:39:8f:67:d5:85:1b:be:ee:5e:ac:f9:6c:47:
6a:f3:95:91:13:23:08:45:a0:f0:b1:55:62:90:ed:
e8:ee:b5:83:29:8a:e3:78:27:31:13:51:33:e4:71:
25:7a:50:34:5f:a8:55:8e:85:70:32:11:29:bb:dc:
33:65:c9:31:a2:3b:5f:12:51:63:01:6d:95:20:37:
52:60:73:2e:49:98:6c:3c:b1:f0:56:ba:fb:6a:5d:
68:19:c0:cd:8f:7b:16:52:2f:44:90:16:97:a3:51:
7f:4f
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:TRUE
X509v3 Subject Key Identifier:
7B:99:3E:6D:8D:80:6F:71:4F:B9:2B:56:F1:E3:3B:E0:A1:7D:16:2B
X509v3 Authority Key Identifier:
keyid:7B:99:3E:6D:8D:80:6F:71:4F:B9:2B:56:F1:E3:3B:E0:A1:7D:16:2B
DirName:/C=GB/ST=England/L=Manchester/O=Scarfolk/OU=Scarfolk/emailAddress=mike.richardson@jisc.ac.uk
serial:1F:87:4B:2F:67:D2:6F:30:81:3A:FD:20:00:4F:03:EB:3D:C4:57:38
X509v3 CRL Distribution Points:
Full Name:
URI:http://crldp.govroam.uk/crldp.crl
Signature Algorithm: sha256WithRSAEncryption
30:63:ea:1f:33:2f:47:ff:2b:49:15:d5:d0:64:97:f1:d9:e0:
be:3a:f8:01:73:ac:7a:79:24:2e:a8:6c:c3:bb:eb:75:fa:88:
c3:e3:49:cc:35:c4:03:f7:ee:ba:32:06:9b:97:b2:82:48:f9:
28:85:8a:97:ee:b6:0f:87:12:79:cf:c9:cb:f9:12:fe:4d:2f:
57:64:52:45:b6:ad:39:c6:b7:07:5e:33:5b:c3:8d:00:26:b1:
0e:08:8e:24:0b:35:48:b4:7b:34:09:37:83:f7:01:2c:ce:12:
4a:48:3a:f6:08:c9:2e:2e:6e:a3:bd:2e:01:ca:16:0d:3f:72:
39:05:86:2c:a0:16:a1:c5:b0:d7:7c:8c:a7:9d:e8:4a:6b:67:
50:ae:7a:12:60:29:04:7e:61:be:fb:e6:c5:97:f2:cb:5c:6d:
fd:41:88:7e:5d:0e:04:52:b4:5e:69:9c:a2:43:1e:c1:a8:8b:
66:76:b1:39:7c:20:df:d8:e9:a2:81:81:be:e5:6c:8a:55:42:
e8:d3:f9:7e:eb:57:44:ab:da:de:c3:c8:01:34:e2:69:2f:d5:
d7:5a:3b:86:d9:c6:b5:e8:08:4c:b3:ed:5c:48:f1:ad:41:ce:
fd:49:27:ac:3c:e4:57:18:e6:ed:0c:1d:0f:8a:2a:0c:c5:e0:
f3:78:b3:98

The Client Certificate

First, the CSR:

openssl req -new -nodes -reqexts client -out newclients/client-req.pem -days 3650 -config ./openssl.cnf

It will prompt for a number of fields. The key one is the hostname. It must be set to the username@realm needed. The realm will be used in the outer ID so should be in the right format for routing. The username part is mostly irrelevant, unless used at other points for authorisation.

Then the cert is signed against the CA:

openssl ca -out newclients/client-cert.pem -extensions client -config ./openssl.cnf -infiles newclients/client-req.pem

Convert the certificate to PKCS12:

openssl pkcs12 -export -out newclients/client-cert.pfx -inkey key.pem -in newclients/client-cert.pem

And you're done.

Example:

Certificate:
Data:
Version: 3 (0x2)
Serial Number: 7 (0x7)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = GB, ST = England, L = Manchester, O = Scarfolk, OU = Scarfolk, emailAddress = mike.richardson@jisc.ac.uk
Validity
Not Before: May 6 13:35:02 2021 GMT
Not After : May 8 13:35:02 2121 GMT
Subject: C = GB, ST = England, L = Manchester, O = Scarfolk, OU = Scarfolk, CN = staff@fr.fr.scarfolk.local
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:d0:59:65:a4:9e:5b:cb:82:cf:e0:39:ac:12:f1:
60:d0:13:3f:76:4b:e7:47:ad:01:f7:c5:a8:2c:61:
8f:49:23:da:54:d8:a9:85:5e:24:53:2c:03:4d:bf:
25:48:65:06:7b:2d:f5:3a:26:9f:f4:3c:d6:53:1d:
8e:a5:81:13:da:c6:23:72:96:97:ca:b2:20:fd:85:
e1:e1:73:71:3a:92:c8:d0:6d:52:cc:48:8d:10:59:
6f:65:7b:fe:ae:fe:66:ff:62:ab:48:a2:b2:c8:03:
aa:38:50:70:43:29:6a:65:30:e8:ee:04:42:66:30:
9b:62:2a:93:41:19:8e:1c:53:f0:9f:59:f4:47:a3:
8b:a5:f0:e4:be:a4:d8:f5:a2:a9:d7:bd:d0:b8:19:
4f:22:2c:15:1c:cf:08:42:65:d3:45:fb:88:b5:5e:
24:14:68:46:8e:0a:c7:66:e7:99:eb:96:08:a9:3e:
48:1f:e9:8b:1d:6d:7a:98:09:a7:3c:4d:5f:9a:3f:
1e:e6:b9:2e:35:0a:07:09:38:23:8b:b4:4b:6a:c6:
65:6a:ca:5e:92:fc:4f:6d:0e:7c:6c:8c:6c:42:54:
74:40:18:b9:bb:0e:5e:37:2f:77:56:0a:95:40:37:
49:d5:f8:e0:a0:dc:23:f3:8f:e9:0a:54:23:e4:da:
83:11
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Cert Type:
SSL Client, SSL Server, S/MIME
Netscape Comment:
OpenSSL Generated Client Certificate
X509v3 Subject Key Identifier:
B8:51:36:FD:01:CD:20:BF:5D:09:52:66:F9:46:F8:35:11:73:E6:AE
X509v3 Key Usage: critical
Digital Signature, Non Repudiation, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection
X509v3 CRL Distribution Points:
Full Name:
URI:http://crldp.govroam.uk/crldp.crl
Signature Algorithm: sha256WithRSAEncryption
81:45:59:55:1d:8c:27:0c:02:0e:44:7e:ef:ab:fc:8c:df:3e:
3e:1d:fc:2b:46:24:c3:65:f5:73:7a:47:b7:89:0b:5a:8c:27:
44:47:ea:90:78:04:d2:fd:9f:56:d7:ff:cb:60:9e:84:f6:bb:
36:e9:ac:7d:8f:c7:ca:a7:05:7a:57:d8:d3:88:fd:b9:87:9b:
6f:20:cc:de:e1:68:9b:81:1b:97:1c:d2:72:c7:d8:a4:c1:84:
54:d3:20:fd:66:19:b2:5d:69:f2:b5:df:20:ba:6e:75:2c:1e:
ae:fe:bb:bc:07:9d:80:ef:42:06:e9:15:d6:0b:07:ff:37:91:
d0:7b:4c:88:bd:22:3d:82:34:3f:22:21:51:d0:55:01:3d:3f:
14:f4:c4:a9:15:36:9e:fa:5b:e0:e7:41:58:34:c1:12:9a:7f:
63:a4:52:97:a3:da:3f:45:6f:00:4e:b1:f5:e1:33:bf:6e:06:
aa:90:f1:75:43:3a:dc:fe:57:f4:5b:e9:b6:f8:a2:3b:d9:e9:
bd:47:a8:7e:4b:bf:4c:c7:28:9e:43:15:ee:f7:ff:29:31:82:
29:49:6d:33:b1:e6:b9:b9:70:3f:86:ac:50:26:35:c4:1d:c5:
9b:02:82:67:b0:94:9e:e0:0a:2a:aa:5e:16:75:cd:8c:90:78:
a6:a5:d0:ed