summaryrefslogtreecommitdiffstats
path: root/raddb/certs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:49:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:49:46 +0000
commit50b37d4a27d3295a29afca2286f1a5a086142cec (patch)
tree9212f763934ee090ef72d823f559f52ce387f268 /raddb/certs
parentInitial commit. (diff)
downloadfreeradius-50b37d4a27d3295a29afca2286f1a5a086142cec.tar.xz
freeradius-50b37d4a27d3295a29afca2286f1a5a086142cec.zip
Adding upstream version 3.2.1+dfsg.upstream/3.2.1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'raddb/certs')
-rw-r--r--raddb/certs/.gitignore13
-rw-r--r--raddb/certs/Makefile186
-rw-r--r--raddb/certs/README.md248
-rwxr-xr-xraddb/certs/bootstrap86
-rw-r--r--raddb/certs/ca.cnf62
-rw-r--r--raddb/certs/client.cnf53
-rw-r--r--raddb/certs/demoCA/cacert.pem22
-rw-r--r--raddb/certs/inner-server.cnf55
-rw-r--r--raddb/certs/ocsp.cnf61
-rw-r--r--raddb/certs/realms/README.md235
-rw-r--r--raddb/certs/server.cnf72
-rw-r--r--raddb/certs/xpextensions75
12 files changed, 1168 insertions, 0 deletions
diff --git a/raddb/certs/.gitignore b/raddb/certs/.gitignore
new file mode 100644
index 0000000..45e1bcb
--- /dev/null
+++ b/raddb/certs/.gitignore
@@ -0,0 +1,13 @@
+*.pem
+*.key
+*.crt
+*.csr
+*.p12
+*.old
+*.attr
+*.crl
+dh
+index.txt
+random
+serial
+passwords.mk
diff --git a/raddb/certs/Makefile b/raddb/certs/Makefile
new file mode 100644
index 0000000..c9fbc9e
--- /dev/null
+++ b/raddb/certs/Makefile
@@ -0,0 +1,186 @@
+######################################################################
+#
+# Make file to be installed in /etc/raddb/certs to enable
+# the easy creation of certificates.
+#
+# See the README file in this directory for more information.
+#
+# $Id$
+#
+######################################################################
+
+DH_KEY_SIZE = 2048
+OPENSSL = openssl
+EXTERNAL_CA = $(wildcard external_ca.*)
+
+ifneq "$(EXTERNAL_CA)" ""
+PARTIAL = -partial_chain
+endif
+
+#
+# Set the passwords
+#
+include passwords.mk
+
+######################################################################
+#
+# Make the necessary files, but not client certificates.
+#
+######################################################################
+.PHONY: all
+all: index.txt serial dh ca server client
+
+.PHONY: client
+client: client.pem
+
+.PHONY: ca
+ca: ca.der ca.crl
+
+.PHONY: server
+server: server.pem server.vrfy
+
+.PHONY: inner-server
+inner-server: inner-server.pem inner-server.vrfy
+
+.PHONY: verify
+verify: server.vrfy client.vrfy
+
+passwords.mk: server.cnf ca.cnf client.cnf inner-server.cnf
+ @echo "PASSWORD_SERVER = '$(shell grep output_password server.cnf | sed 's/.*=//;s/^ *//')'" > $@
+ @echo "PASSWORD_INNER = '$(shell grep output_password inner-server.cnf | sed 's/.*=//;s/^ *//')'" >> $@
+ @echo "PASSWORD_CA = '$(shell grep output_password ca.cnf | sed 's/.*=//;s/^ *//')'" >> $@
+ @echo "PASSWORD_CLIENT = '$(shell grep output_password client.cnf | sed 's/.*=//;s/^ *//')'" >> $@
+ @echo "USER_NAME = '$(shell grep emailAddress client.cnf | grep '@' | sed 's/.*=//;s/^ *//')'" >> $@
+ @echo "CA_DEFAULT_DAYS = '$(shell grep default_days ca.cnf | sed 's/.*=//;s/^ *//')'" >> $@
+
+######################################################################
+#
+# Diffie-Hellman parameters
+#
+######################################################################
+dh:
+ $(OPENSSL) dhparam -out dh -2 $(DH_KEY_SIZE)
+
+######################################################################
+#
+# Create a new self-signed CA certificate
+#
+######################################################################
+ca.key ca.pem: ca.cnf
+ @[ -f index.txt ] || $(MAKE) index.txt
+ @[ -f serial ] || $(MAKE) serial
+ $(OPENSSL) req -new -x509 -keyout ca.key -out ca.pem \
+ -days $(CA_DEFAULT_DAYS) -config ./ca.cnf \
+ -passin pass:$(PASSWORD_CA) -passout pass:$(PASSWORD_CA)
+ chmod g+r ca.key
+
+ca.der: ca.pem
+ $(OPENSSL) x509 -inform PEM -outform DER -in ca.pem -out ca.der
+
+ca.crl: ca.pem
+ $(OPENSSL) ca -gencrl -keyfile ca.key -cert ca.pem -config ./ca.cnf -out ca-crl.pem -key $(PASSWORD_CA)
+ $(OPENSSL) crl -in ca-crl.pem -outform der -out ca.crl
+ rm ca-crl.pem
+
+######################################################################
+#
+# Create a new server certificate, signed by the above CA.
+#
+######################################################################
+server.csr server.key: server.cnf
+ $(OPENSSL) req -new -out server.csr -keyout server.key -config ./server.cnf
+ chmod g+r server.key
+
+server.crt: ca.key ca.pem server.csr
+ $(OPENSSL) ca -batch -keyfile ca.key -cert ca.pem -in server.csr -key $(PASSWORD_CA) -out server.crt -extensions xpserver_ext -extfile xpextensions -config ./server.cnf
+
+server.p12: server.crt
+ $(OPENSSL) pkcs12 -export -in server.crt -inkey server.key -out server.p12 -passin pass:$(PASSWORD_SERVER) -passout pass:$(PASSWORD_SERVER)
+ chmod g+r server.p12
+
+server.pem: server.p12
+ $(OPENSSL) pkcs12 -in server.p12 -out server.pem -passin pass:$(PASSWORD_SERVER) -passout pass:$(PASSWORD_SERVER)
+ chmod g+r server.pem
+
+.PHONY: server.vrfy
+server.vrfy: ca.pem
+ @$(OPENSSL) verify $(PARTIAL) -CAfile ca.pem server.pem
+
+######################################################################
+#
+# Create a new client certificate, signed by the the above server
+# certificate.
+#
+######################################################################
+client.csr client.key: client.cnf
+ $(OPENSSL) req -new -out client.csr -keyout client.key -config ./client.cnf
+ chmod g+r client.key
+
+client.crt: ca.key ca.pem client.csr
+ $(OPENSSL) ca -batch -keyfile ca.key -cert ca.pem -in client.csr -key $(PASSWORD_CA) -out client.crt -extensions xpclient_ext -extfile xpextensions -config ./client.cnf
+
+client.p12: client.crt
+ $(OPENSSL) pkcs12 -export -in client.crt -inkey client.key -out client.p12 -passin pass:$(PASSWORD_CLIENT) -passout pass:$(PASSWORD_CLIENT)
+ chmod g+r client.p12
+ cp client.p12 $(USER_NAME).p12
+
+client.pem: client.p12
+ $(OPENSSL) pkcs12 -in client.p12 -out client.pem -passin pass:$(PASSWORD_CLIENT) -passout pass:$(PASSWORD_CLIENT)
+ chmod g+r client.pem
+ cp client.pem $(USER_NAME).pem
+
+.PHONY: client.vrfy
+client.vrfy: ca.pem client.pem
+ c_rehash .
+ $(OPENSSL) verify -CApath . client.pem
+
+######################################################################
+#
+# Create a new inner-server certificate, signed by the above CA.
+#
+######################################################################
+inner-server.csr inner-server.key: inner-server.cnf
+ $(OPENSSL) req -new -out inner-server.csr -keyout inner-server.key -config ./inner-server.cnf
+ chmod g+r inner-server.key
+
+inner-server.crt: ca.key ca.pem inner-server.csr
+ $(OPENSSL) ca -batch -keyfile ca.key -cert ca.pem -in inner-server.csr -key $(PASSWORD_CA) -out inner-server.crt -extensions xpserver_ext -extfile xpextensions -config ./inner-server.cnf
+
+inner-server.p12: inner-server.crt
+ $(OPENSSL) pkcs12 -export -in inner-server.crt -inkey inner-server.key -out inner-server.p12 -passin pass:$(PASSWORD_INNER) -passout pass:$(PASSWORD_INNER)
+ chmod g+r inner-server.p12
+
+inner-server.pem: inner-server.p12
+ $(OPENSSL) pkcs12 -in inner-server.p12 -out inner-server.pem -passin pass:$(PASSWORD_INNER) -passout pass:$(PASSWORD_INNER)
+ chmod g+r inner-server.pem
+
+.PHONY: inner-server.vrfy
+inner-server.vrfy: ca.pem
+ @$(OPENSSL) verify $(PARTIAL) -CAfile ca.pem inner-server.pem
+
+######################################################################
+#
+# Miscellaneous rules.
+#
+######################################################################
+index.txt:
+ @touch index.txt
+
+serial:
+ @echo '01' > serial
+
+print:
+ $(OPENSSL) x509 -text -in server.crt
+
+printca:
+ $(OPENSSL) x509 -text -in ca.pem
+
+clean:
+ @rm -f *~ *old client.csr client.key client.crt client.p12 client.pem
+
+#
+# Make a target that people won't run too often.
+#
+destroycerts:
+ rm -f *~ dh *.csr *.crt *.p12 *.der *.pem *.key index.txt* \
+ serial* *\.0 *\.1 ca-crl.pem ca.crl
diff --git a/raddb/certs/README.md b/raddb/certs/README.md
new file mode 100644
index 0000000..1d73f99
--- /dev/null
+++ b/raddb/certs/README.md
@@ -0,0 +1,248 @@
+# Certificate Documentation
+
+This directory contains scripts to create the server certificates. To
+make a set of default (i.e. test) certificates, simply type:
+
+```
+$ ./bootstrap
+```
+
+The `openssl` command will be run against the sample configuration
+files included here, and will make a self-signed certificate authority
+(i.e. root CA), and a server certificate. This "root CA" should be
+installed on any client machine needing to do EAP-TLS, PEAP, or
+EAP-TTLS.
+
+The Extended Key Usage (EKU) fields for "TLS web server" will be
+automatically included in the server certificate. Without those
+extensions many clients will refuse to authenticate to FreeRADIUS.
+
+The root CA and the "XP Extensions" file also contain a
+crlDistributionPoints attribute. Many systems need this to be present
+in order to validate the RADIUS server certificate. The RADIUS server
+must have the URI defined but the CA need not have...however it is
+best practice for a CA to have a revocation URI. Note that whilst the
+Windows Mobile client cannot actually use the CRL when doing 802.1X it
+is recommended that the URI be an actual working URL and contain a
+revocation format file as there may be other OS behaviour at play and
+future OSes that may do something with that URI.
+
+For Windows, you will need to import the `p12` and/or the `der` format
+of the certificates. Linux systems need the `pem` format.
+
+In general, you should use self-signed certificates for 802.1X (EAP)
+authentication. When you list root CAs from other organisations in
+the `ca_file`, you permit them to masquerade as you, to authenticate
+your users, and to issue client certificates for EAP-TLS.
+
+If you already have CA and server certificates, rename (or delete)
+this directory, and create a new `certs` directory containing your
+certificates. Note that the `make install` command will **not**
+over-write your existing `raddb/certs` directory.
+
+
+## New Installations of FreeRADIUS
+
+We suggest that new installations use the test certificates for
+initial tests, and then create real certificates to use for normal
+user authentication. See the instructions below for how to create the
+various certificates. The old test certificates can be deleted by
+running the following command:
+
+```
+$ make destroycerts
+```
+
+Then, follow the instructions below for creating real certificates.
+
+If you do not want to enable EAP-TLS, PEAP, or EAP-TTLS, then delete
+the relevant sub-sections from the `raddb/mods-available/eap` file.
+See the comments in that file for more information.
+
+
+## Making a root Certificate
+
+We recommend using a private certificate authority (CA). While it can
+be difficult to install this CA on multiple client machines, it is (in
+general) more secure.
+
+```
+$ vi ca.cnf
+```
+
+Edit `default_days` to set the desired lifetime of the CA certificate.
+
+Edit the `input_password` and `output_password` fields to be the
+password for the CA certificate.
+
+Edit the `[certificate_authority]` section to have the correct values
+for your country, state, etc.
+
+Create the CA certificate:
+
+```
+$ make ca.pem
+```
+
+Then the `DER` format needed by Windows:
+
+```
+$ make ca.der
+```
+
+
+## Making a Server Certificate
+
+The following steps will let you create a server certificate for use
+with TLS-based EAP methods, such as EAP-TLS, PEAP, and TTLS. Follow
+similar steps to create an `inner-server.pem` file, for use with
+EAP-TLS that is tunneled inside of another TLS-based EAP method.
+
+```
+$ vi server.cnf
+```
+
+Edit `default_days` to set the lifetime of the server certificate.
+The maximum for this is 825 for compatibility with all client devices.
+
+Edit the `input_password` and `output_password` fields to be the
+password for the server certificate.
+
+Edit the `[server]` section to have the correct values for your
+country, state, etc. Be sure that the `commonName` field here is
+different from the `commonName` for the CA certificate.
+
+Create the server certificate:
+
+```
+$ make server
+```
+
+
+### Making a certificate for a public CA
+
+If you wish to use an existing certificate authority, you can
+create a certificate signing request for the server certificate, edit
+`server.cnf` as above, and run the following command.
+
+```
+$ make server.csr
+```
+
+This step creates a "Certificate Signing Request" suitable for
+submission to a public CA.
+
+
+## Making a Client certificate
+
+Client certificates are used by EAP-TLS, and optionally by EAP-TTLS
+and PEAP. The following steps outline how to create a client
+certificate that is signed by the CA certificate created above. You
+will have to have the password for the CA certificate in the
+`input_password` and `output_password` fields of the `ca.cnf` file.
+
+```
+$ vi client.cnf
+```
+
+Edit `default_days` to set the lifetime of the client certificate.
+
+Edit the `input_password` and `output_password` fields to be the
+password for the client certificate. You will have to give these
+passwords to the end user who will be using the certificates.
+
+Edit the `[client]` section to have the correct values for your
+country, state, etc. Be sure that the `commonName` field here is
+the `User-Name` which will be used for logins!
+
+```
+$ make client
+```
+
+The users certificate will be in `emailAddress.pem`,
+e.g. `user@example.com.pem`.
+
+To create another client certificate, just repeat the steps for
+making a client certificate, being sure to enter a different login
+name for `commonName`, and a different password.
+
+
+## Performance
+
+EAP performance for EAP-TLS, TTLS, and PEAP is dominated by SSL
+calculations. That is, a normal system can handle PAP
+authentication at a rate of 10k packets/s. However, SSL involves
+RSA calculations, which are very expensive. To benchmark your system,
+do:
+
+```
+$ openssl speed rsa
+```
+
+or
+
+```
+$ openssl speed rsa2048
+```
+
+to test 2048 bit keys.
+
+The number that is printed is the **maximum** number of
+authentications per second which can be done for EAP-TLS (or TTLS,
+or PEAP). In practice, you will see results much lower than this
+number, i.e. the actual EAP-TLS performance may be half of the
+number printed here.
+
+The reason is that EAP requires many round-trip packets, whereas
+`openssl speed rsa2028` only does RSA calculations, and nothing else.
+
+
+## Compatibility
+
+The certificates created using this method are known to be compatible
+with ALL operating systems. Some common issues are:
+
+* iOS and macOS have requirements on certificates. See:
+ https://support.apple.com/en-us/HT210176
+
+* Many systems require certain OIDs in the certificates
+ (`id-kp-serverAuth` for `TLS Web server authentication`).
+ If the certificate does not contain these fields, the system
+ will stop doing EAP. The most visible effect is that the client
+ starts EAP, gets a few Access-Challenge packets, and then a little
+ while later re-starts EAP. If this happens, see the FAQ, and the
+ comments in `raddb/mods-available/eap` for how to fix it.
+
+* All systems requires the root certificates to be on the client PC.
+ If it doesn't have them, you will see the same issue as above.
+
+* Windows XP post SP2 has a bug where it has problems with
+ certificate chains. i.e. if the server certificate is an
+ intermediate one, and not a root one, then authentication
+ will silently fail, as above.
+
+* Some versions of Windows CE cannot handle 4K RSA certificates.
+ They will (again) silently fail, as above.
+
+* In none of these cases will Windows give the end user any
+ reasonable error message describing what went wrong. This leads
+ people to blame the RADIUS server. That blame is misplaced.
+
+* Certificate chains of more than 64K bytes are known to not work.
+ This is partly a problem in FreeRADIUS. However, most clients cannot
+ handle 64K certificate chains. Most Access Points will shut down the
+ EAP session after about 50 round trips, while 64K certificate chains
+ will take about 60 round trips. So don't use large certificate
+ chains. They will only work after everyone upgrades everything in the
+ network.
+
+* All other operating systems are known to work with EAP and
+ FreeRADIUS. This includes Linux, the BSDs, macOS, iOS, Android,
+ Solaris, Symbian, along with all known embedded systems, phones,
+ WiFi devices, etc.
+
+
+## Security Considerations
+
+The default certificate configuration files uses SHA256 for message
+digests for security.
diff --git a/raddb/certs/bootstrap b/raddb/certs/bootstrap
new file mode 100755
index 0000000..57de8cf
--- /dev/null
+++ b/raddb/certs/bootstrap
@@ -0,0 +1,86 @@
+#!/bin/sh
+#
+# This is a wrapper script to create default certificates when the
+# server first starts in debugging mode. Once the certificates have been
+# created, this file should be deleted.
+#
+# Ideally, this program should be run as part of the installation of any
+# binary package. The installation should also ensure that the permissions
+# and owners are correct for the files generated by this script.
+#
+# $Id$
+#
+umask 027
+cd `dirname $0`
+
+make -h > /dev/null 2>&1
+
+#
+# If we have a working "make", then use it. Otherwise, run the commands
+# manually.
+#
+if [ "$?" = "0" ]; then
+ make all
+ exit $?
+fi
+
+#
+# The following commands were created by running "make -n", and edited
+# to remove the trailing backslash, and to add "exit 1" after the commands.
+#
+# Don't edit the following text. Instead, edit the Makefile, and
+# re-generate these commands.
+#
+if [ ! -f dh ]; then
+ openssl dhparam -out dh 2048 || exit 1
+ if [ -e /dev/urandom ] ; then
+ ln -sf /dev/urandom random
+ else
+ date > ./random;
+ fi
+fi
+
+if [ ! -f server.key ]; then
+ openssl req -new -out server.csr -keyout server.key -config ./server.cnf || exit 1
+ chmod g+r server.key
+fi
+
+if [ ! -f ca.key ]; then
+ openssl req -new -x509 -keyout ca.key -out ca.pem -days `grep default_days ca.cnf | sed 's/.*=//;s/^ *//'` -config ./ca.cnf || exit 1
+fi
+
+if [ ! -f index.txt ]; then
+ touch index.txt
+fi
+
+if [ ! -f serial ]; then
+ echo '01' > serial
+fi
+
+if [ ! -f server.crt ]; then
+ openssl ca -batch -keyfile ca.key -cert ca.pem -in server.csr -key `grep output_password ca.cnf | sed 's/.*=//;s/^ *//'` -out server.crt -extensions xpserver_ext -extfile xpextensions -config ./server.cnf || exit 1
+fi
+
+if [ ! -f server.p12 ]; then
+ openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12 -passin pass:`grep output_password server.cnf | sed 's/.*=//;s/^ *//'` -passout pass:`grep output_password server.cnf | sed 's/.*=//;s/^ *//'` || exit 1
+ chmod g+r server.p12
+fi
+
+if [ ! -f server.pem ]; then
+ openssl pkcs12 -in server.p12 -out server.pem -passin pass:`grep output_password server.cnf | sed 's/.*=//;s/^ *//'` -passout pass:`grep output_password server.cnf | sed 's/.*=//;s/^ *//'` || exit 1
+ openssl verify -CAfile ca.pem server.pem || exit 1
+ chmod g+r server.pem
+fi
+
+if [ ! -f ca.der ]; then
+ openssl x509 -inform PEM -outform DER -in ca.pem -out ca.der || exit 1
+fi
+
+if [ ! -f client.key ]; then
+ openssl req -new -out client.csr -keyout client.key -config ./client.cnf
+ chmod g+r client.key
+fi
+
+if [ ! -f client.crt ]; then
+ openssl ca -batch -keyfile ca.key -cert ca.pem -in client.csr -key `grep output_password ca.cnf | sed 's/.*=//;s/^ *//'` -out client.crt -extensions xpclient_ext -extfile xpextensions -config ./client.cnf
+fi
diff --git a/raddb/certs/ca.cnf b/raddb/certs/ca.cnf
new file mode 100644
index 0000000..d49991b
--- /dev/null
+++ b/raddb/certs/ca.cnf
@@ -0,0 +1,62 @@
+[ ca ]
+default_ca = CA_default
+
+[ CA_default ]
+dir = ./
+certs = $dir
+crl_dir = $dir/crl
+database = $dir/index.txt
+new_certs_dir = $dir
+certificate = $dir/ca.pem
+serial = $dir/serial
+crl = $dir/crl.pem
+private_key = $dir/ca.key
+RANDFILE = $dir/.rand
+name_opt = ca_default
+cert_opt = ca_default
+default_days = 60
+default_crl_days = 30
+default_md = sha256
+preserve = no
+policy = policy_match
+crlDistributionPoints = URI:http://www.example.org/example_ca.crl
+
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ req ]
+prompt = no
+distinguished_name = certificate_authority
+default_bits = 2048
+input_password = whatever
+output_password = whatever
+x509_extensions = v3_ca
+
+[certificate_authority]
+countryName = FR
+stateOrProvinceName = Radius
+localityName = Somewhere
+organizationName = Example Inc.
+emailAddress = admin@example.org
+commonName = "Example Certificate Authority"
+
+[v3_ca]
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+basicConstraints = critical,CA:true
+crlDistributionPoints = URI:http://www.example.org/example_ca.crl
+
diff --git a/raddb/certs/client.cnf b/raddb/certs/client.cnf
new file mode 100644
index 0000000..2650e47
--- /dev/null
+++ b/raddb/certs/client.cnf
@@ -0,0 +1,53 @@
+[ ca ]
+default_ca = CA_default
+
+[ CA_default ]
+dir = ./
+certs = $dir
+crl_dir = $dir/crl
+database = $dir/index.txt
+new_certs_dir = $dir
+certificate = $dir/ca.pem
+serial = $dir/serial
+crl = $dir/crl.pem
+private_key = $dir/ca.key
+RANDFILE = $dir/.rand
+name_opt = ca_default
+cert_opt = ca_default
+default_days = 60
+default_crl_days = 30
+default_md = sha256
+preserve = no
+policy = policy_match
+
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ req ]
+prompt = no
+distinguished_name = client
+default_bits = 2048
+input_password = whatever
+output_password = whatever
+
+[client]
+countryName = FR
+stateOrProvinceName = Radius
+localityName = Somewhere
+organizationName = Example Inc.
+emailAddress = user@example.org
+commonName = user@example.org
diff --git a/raddb/certs/demoCA/cacert.pem b/raddb/certs/demoCA/cacert.pem
new file mode 100644
index 0000000..7ddff4d
--- /dev/null
+++ b/raddb/certs/demoCA/cacert.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDtjCCAx+gAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnzELMAkGA1UEBhMCQ0Ex
+ETAPBgNVBAgTCFByb3ZpbmNlMRIwEAYDVQQHEwlTb21lIENpdHkxFTATBgNVBAoT
+DE9yZ2FuaXphdGlvbjESMBAGA1UECxMJbG9jYWxob3N0MRswGQYDVQQDExJDbGll
+bnQgY2VydGlmaWNhdGUxITAfBgkqhkiG9w0BCQEWEmNsaWVudEBleGFtcGxlLmNv
+bTAeFw0wNDAxMjUxMzI2MDdaFw0wNjAxMjQxMzI2MDdaMIGfMQswCQYDVQQGEwJD
+QTERMA8GA1UECBMIUHJvdmluY2UxEjAQBgNVBAcTCVNvbWUgQ2l0eTEVMBMGA1UE
+ChMMT3JnYW5pemF0aW9uMRIwEAYDVQQLEwlsb2NhbGhvc3QxGzAZBgNVBAMTEkNs
+aWVudCBjZXJ0aWZpY2F0ZTEhMB8GCSqGSIb3DQEJARYSY2xpZW50QGV4YW1wbGUu
+Y29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUxbGXJPFkrPH/sYnbHI+/
+9PFDlup8sekPeNaUUXJTd4ld/lLMuZtB6A3etYsSepQ/T1jLxWKHgZL73G/s6fhx
+58Ew01z1GIgX6bEzJJ7dKhx10xBDrodVPOx6d+8mqn10KB25t34XxkRsXdmxiLQy
+UMoCKZY3IqEjpyawC0An/QIDAQABo4H/MIH8MB0GA1UdDgQWBBRo020+Hue8nVoF
+cCHDY9oTZdGt4zCBzAYDVR0jBIHEMIHBgBRo020+Hue8nVoFcCHDY9oTZdGt46GB
+paSBojCBnzELMAkGA1UEBhMCQ0ExETAPBgNVBAgTCFByb3ZpbmNlMRIwEAYDVQQH
+EwlTb21lIENpdHkxFTATBgNVBAoTDE9yZ2FuaXphdGlvbjESMBAGA1UECxMJbG9j
+YWxob3N0MRswGQYDVQQDExJDbGllbnQgY2VydGlmaWNhdGUxITAfBgkqhkiG9w0B
+CQEWEmNsaWVudEBleGFtcGxlLmNvbYIBADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
+DQEBBAUAA4GBADPAC2ax5Xnvc6BnmCUtq41eVRH8AP0nbYDRL4NHd8Z0P9wnQ/yh
+UHcE5LwJeeT2CsOtnug+bzRzaSKdH3cim6LpgjWdpWMCSgAWPbptbJhsC60or4UT
+L/jw12UBvxt8Lf9ljOHmLAGZe25k4+jUNzNUzpkShHZRU5BjuFu8VIXF
+-----END CERTIFICATE-----
diff --git a/raddb/certs/inner-server.cnf b/raddb/certs/inner-server.cnf
new file mode 100644
index 0000000..2101c2e
--- /dev/null
+++ b/raddb/certs/inner-server.cnf
@@ -0,0 +1,55 @@
+[ ca ]
+default_ca = CA_default
+
+[ CA_default ]
+dir = ./
+certs = $dir
+crl_dir = $dir/crl
+database = $dir/index.txt
+new_certs_dir = $dir
+certificate = $dir/server.pem
+serial = $dir/serial
+crl = $dir/crl.pem
+private_key = $dir/server.key
+RANDFILE = $dir/.rand
+name_opt = ca_default
+cert_opt = ca_default
+default_days = 60
+default_crl_days = 30
+default_md = sha256
+preserve = no
+policy = policy_match
+copy_extensions = copy
+
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ req ]
+prompt = no
+distinguished_name = server
+default_bits = 2048
+input_password = whatever
+output_password = whatever
+
+[server]
+countryName = FR
+stateOrProvinceName = Radius
+localityName = Somewhere
+organizationName = Example Inc.
+emailAddress = admin@example.org
+commonName = "Example Inner Server Certificate"
+
diff --git a/raddb/certs/ocsp.cnf b/raddb/certs/ocsp.cnf
new file mode 100644
index 0000000..01225d8
--- /dev/null
+++ b/raddb/certs/ocsp.cnf
@@ -0,0 +1,61 @@
+[ ca ]
+default_ca = CA_default
+
+[ CA_default ]
+dir = ./
+certs = $dir
+crl_dir = $dir/crl
+database = $dir/index.txt
+new_certs_dir = $dir
+certificate = $dir/server.pem
+serial = $dir/serial
+crl = $dir/crl.pem
+private_key = $dir/server.key
+RANDFILE = $dir/.rand
+name_opt = ca_default
+cert_opt = ca_default
+default_days = 60
+default_crl_days = 30
+default_md = sha256
+preserve = no
+policy = policy_match
+unique_subject = no
+x509_extensions = v3_ocsp
+
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ req ]
+prompt = no
+distinguished_name = server
+default_bits = 2048
+input_password = whatever
+output_password = whatever
+
+[ server ]
+countryName = FR
+stateOrProvinceName = Radius
+localityName = Somewhere
+organizationName = Example Inc.
+emailAddress = admin@example.org
+commonName = "Example OCSP Responder Certificate"
+subjectAltName = ocsp.example.org
+
+[ v3_ocsp ]
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
diff --git a/raddb/certs/realms/README.md b/raddb/certs/realms/README.md
new file mode 100644
index 0000000..9f754fa
--- /dev/null
+++ b/raddb/certs/realms/README.md
@@ -0,0 +1,235 @@
+# Multiple Certificate Chains
+
+As of version 3.0.24, FreeRADIUS supports loading multiple certificate
+chains, keyed by a realm name. These chains are in _addition_ to the
+default certificates loaded by the `tls` section.
+
+Loading multiple certificate chains means that the server can have
+different identities. i.e. When a user `bob@example.com` requests
+network access, the server can present an `example.com` certificate.
+On the other hand, when a user `doug@example.org` requests network
+access, the server cna present an `example.org` certificate.
+
+This functionality means that it is possible to configure only one
+`eap` module, and then use multiple certificate chains. Previous
+versions of the server required that the administrator configure
+multiple EAP modules, one for each certificate being used.
+
+The selection can be performed in one of two ways. First, the
+certificates can be loaded dynamically at run-time. Second, the
+certificates can be pre-loaded for speed.
+
+## Dynamic Loading of Certificate Chains
+
+The server can dynamically load a certificate chain by setting a
+special attribute. This has to be done _after_ the server has
+received the EAP identity request, and _before_ the TLS session setup
+has started.
+
+The simplest way to do this is via the following `unlang` statements:
+
+```
+authenticate {
+ ...
+ Auth-Type eap {
+ if ("%{unpack:&EAP-Message 4 byte}" == 1) {
+ update control {
+ TLS-Session-Cert-File := "${certdir}/realms/%{Realm}"
+ }
+ }
+
+ eap
+ }
+ ...
+}
+```
+
+This configuration looks at the `EAP-Message` attribute, and checks if
+it is an EAP-Identity packet. If so, it then adds a special attribute
+`TLS-Session-Cert-File`, with a value based on the `Realm`, from the
+`User-Name`. That setting tells the server to look in the file for a
+certificate.
+
+If the file is there, and contains a correctly formatted `PEM`
+certificate chain, then it is loaded and used.
+
+If the file does not exist, or the file does not contain a correctly
+formatted `PEM` certificate chain, then the user is rejected.
+
+### Format
+
+This file should contain the server certificate, followed by
+intermediate certificates, in order. i.e. If we have a server
+certificate signed by CA1, which is signed by CA2, which is signed by
+a root CA, then the "certificate_file" should contain server.pem,
+followed by CA1.pem, followed by CA2.pem.
+
+When using `ca_file` or `ca_dir`, the file should contain only the
+server certificate.
+
+### Private Key
+
+The private should be placed in the same file as the other
+certificates, but at the start.
+
+```
+private key
+server cert
+...
+ca cert
+```
+
+The private key can also be placed into a separate file. The filename
+should be placed into the `TLS-Session-Cert-Private-Key-File`
+attribute.
+
+For simplicity, the private keys _should not_ have passwords. There
+is essentially no security benefit to "securing" the key with a
+password, and then placing the password into the file system, right
+next to the private key.
+
+### Realms
+
+There is no need to place the certificates into files named for each
+realm. However, it is by far and away the easiest way to manage these
+certificate chains.
+
+For every realm which is handles this way, the `proxy.conf` file
+should define a _local_ realm. That is, it should contain a
+definition such as:
+
+```
+example.org {
+}
+```
+
+This defines the realm `example.org`, and tells FreeRADIUS that there
+are no home servers associated with the realm.
+
+The `suffix` module should also be configured, as per the default
+configuration. i.e. list `suffix` in the `authorize` section _before_
+the `eap` module.
+
+### Caveats
+
+The root CA certificate for the server certificate should be located
+in the `ca_dir`, along with other root CAs. If the root CA is not
+there, then it *must* be included at the end of the file.
+
+These certificates will be loaded and parsed _for every matching
+authentication request_. That limitation means that dynamic loading
+of the certificates is likely be slow, and to severely impact
+performance. The good news is that we can fix that with a little more
+configuration.
+
+## Preloading Certificate Chains
+
+The server can also pre-load certificate chains. In the EAP module,
+you can do:
+
+```
+eap {
+ ...
+ tls {
+ ...
+ realm_dir = ${certdir}/realms/
+ ...
+ }
+ ...
+}
+```
+
+Each file in that directory should be a `PEM` encoded certificate
+chain, as described in the previous section. For safety, every file
+*must* have a `.pem` as the filename extension.
+e.g. `example.org.pem`.
+
+If there is a corresponding private key, it should be placed into a
+`.key` file. e.g. `example.org.key`.
+
+These certificates will be loaded when the server starts, and cached
+for the lifetime of the server. There is no way to reload these
+certificates dynamically, the server must be restarted.
+
+Once the `realm_dir` configuration has been added, the selection of
+certificates is identical to that described in the previous section.
+Just set `TLS-Session-Cert-File`, and the server will figure it out.
+
+However, it is possible to dynamically add new certificate, and have
+the server pick it up. In fact, as the process for choosing
+certificates are the same, the server will do this automatically!
+
+## RadSec
+
+The above configuration applies to RadSec, too, as the `tls`
+configuration in the server is for all TLS functionality, and not just
+EAP.
+
+This means that the server can accept RadSec connections, and then
+present different server certificates to different clients.
+
+For this functionality to work, the certificates for EAP and RadSec
+*should* be in separate directories.
+
+### Clients
+
+RadSec clients can set the SNI to send in the `tls` subsection of the
+`home_server` definition. Look for "SNI" in `sites-available/tls`,
+and see the `hostname` configuration item for documentation.
+
+For example, an identity provider could host multiple sites, but
+present itself with one public IP address. If the RadSec clients do
+not use SNI, then they must be configured with the certificate of the
+identity provider.
+
+When SNI is used, the RadSec clients can be configured with the
+certificate of the hosted system that they're connecting to. This
+ability means that there is no need to change certificates when
+changing providers. In addition, there is no need to change the
+configuration of all RadSec clients when the hosting system changes
+its certifiates. Because the hosting system certificates are never
+used.
+
+Instead, each hosted company is responsible for its own certificates,
+and for its own RadSec clients.
+
+SNI also permits the use of a load balancer such as haproxy. That
+load balancer can terminate the TLS connection, and then use SNI to
+route the underlying RADIUS TCP traffic to a particular host.
+
+### Servers
+
+See the `realm_dir` configuration item in the `tls` subsection for the
+location of the server certificates.
+
+If the server receives an SNI for a realm it does not recognize, it
+will just use the default TLS configuration.
+
+If the realm is recognized (i.e. there is a file in
+`${realm_dir}/%{TLS-Server-Name-Indication}.pem`, then that certificate will be chosen, and
+present to the RadSec client. If there is no such file, then the
+default TLS configuration is used.
+
+The current behavior is to _require_ that the server certificate is in
+a file which taken from
+`${realm_dir}/%{TLS-Server-Name-Indication}.pem`. Only the
+`realm_dir` portion of the filename is configurable. The SNI portion
+is taken from the TLS messages, and the `.pem` suffix is hard-coded in
+the source code.
+
+Taking the filename from an untrusted source is fine here. The
+standard (RFC 6066 Section 3) says that the Server Name Indication
+field is a DNS "A label". Which means that there are a limited number
+of characters allowed:
+
+* `.`, `-`, `a-Z`, `A-Z`, `0-9`
+
+If the SNI contain anything else, the TLS connection is rejected.
+
+Note that if session resumption is enabled for RadSec, the session
+cache *must* also cache the `TLS-Server-Name-Indication` attribute.
+The SNI is sent on resumption for TLS 1.2 and earlier, but it is not
+sent for TLS 1.3. As such, the only way to select the right policy on
+resumption is to check the value of the cached
+TLS-Server-Name-Indication attribute.
+
diff --git a/raddb/certs/server.cnf b/raddb/certs/server.cnf
new file mode 100644
index 0000000..daca18d
--- /dev/null
+++ b/raddb/certs/server.cnf
@@ -0,0 +1,72 @@
+[ ca ]
+default_ca = CA_default
+
+[ CA_default ]
+dir = ./
+certs = $dir
+crl_dir = $dir/crl
+database = $dir/index.txt
+new_certs_dir = $dir
+certificate = $dir/server.pem
+serial = $dir/serial
+crl = $dir/crl.pem
+private_key = $dir/server.key
+RANDFILE = $dir/.rand
+name_opt = ca_default
+cert_opt = ca_default
+default_days = 60
+default_crl_days = 30
+default_md = sha256
+preserve = no
+policy = policy_match
+copy_extensions = copy
+
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ req ]
+prompt = no
+distinguished_name = server
+default_bits = 2048
+input_password = whatever
+output_password = whatever
+#req_extensions = v3_req
+
+[server]
+countryName = FR
+stateOrProvinceName = Radius
+localityName = Somewhere
+organizationName = Example Inc.
+emailAddress = admin@example.org
+commonName = "Example Server Certificate"
+
+[ v3_req ]
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName = @alt_names
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+
+# This should be a host name of the RADIUS server.
+# Note that the host name is exchanged in EAP *before*
+# the user machine has network access. So the host name
+# here doesn't really have to match anything in DNS.
+[alt_names]
+DNS.1 = radius.example.com
+
+# NAIRealm from RFC 7585
+otherName.0 = 1.3.6.1.5.5.7.8.8;FORMAT:UTF8,UTF8:*.example.com
diff --git a/raddb/certs/xpextensions b/raddb/certs/xpextensions
new file mode 100644
index 0000000..ae87f42
--- /dev/null
+++ b/raddb/certs/xpextensions
@@ -0,0 +1,75 @@
+#
+# File containing the OIDs required for Windows
+# and iOS
+#
+# http://support.microsoft.com/kb/814394/en-us
+#
+# https://support.apple.com/en-us/HT210176
+#
+[ xpclient_ext]
+extendedKeyUsage = 1.3.6.1.5.5.7.3.2
+crlDistributionPoints = URI:http://www.example.com/example_ca.crl
+
+[ xpserver_ext]
+extendedKeyUsage = 1.3.6.1.5.5.7.3.1
+crlDistributionPoints = URI:http://www.example.com/example_ca.crl
+
+# Enterprise Wi-Fi clients from 2020 onwards which have the
+# Wi-Fi Certified WPA3 Release 2 (December 2019) certification
+# honour the following two policies for enhanced security
+# posture regarding certificate validation:
+#
+# https://www.wi-fi.org/discover-wi-fi/security
+#
+# Adding the 'Trust Override Disabled - STRICT' policy means that
+# the client device is not allowed to request and accept ad-hoc
+# trust decisions from the user ("Is this the certificate you
+# expect here?") and instead aborts authentication until the
+# device has been properly configured using out-of-band means
+# with all the details needed to verify the certificate (i.e.
+# either the tuple (CA, server name) or the literal server cert).
+#
+# Adding the 'Trust Override Disabled - TOFU' policy means that
+# the client device is allowed to ask the end user for such an
+# override exactly once, when first connecting to an unknown
+# network. Once the network is known and the trust decision made,
+# any other certificate that is presented and would require
+# another override is rejected and authentication aborted.
+#
+# Both of these policies provide a protection against rogue
+# authentication servers in that they make sure configurations
+# on end user devices are sufficient to identify the genuine
+# server.
+#
+# The difference is that the TOFU policy allows a leap of faith
+# on first sight of a network ONCE - very much comparable to
+# how SSH establishes trust in a new host. This adds convenience
+# for end users who did not bother to configure their devices
+# beforehand, but adds an element of uncertainty in that the
+# attacker could be present on that first contact with the network.
+#
+# Network administrators who consider the TOFU leap of faith
+# unacceptable should choose STRICT; everyone else gains security
+# by choosing TOFU without giving up on convenience for their
+# end users.
+#
+# For completeness, it is also possible to include none of the
+# two to stay with the "anything goes" that was the situation
+# prior to Wi-Fi Certified WPA3 Release December 2019.
+#
+# This is the 'Trust Override Disabled - STRICT' policy.
+#certificatePolicies = 1.3.6.1.4.1.40808.1.3.1
+# This is the 'Trust Override Disabled - TOFU' policy.
+certificatePolicies = 1.3.6.1.4.1.40808.1.3.2
+
+#
+# Add this to the PKCS#7 keybag attributes holding the client's private key
+# for machine authentication.
+#
+# the presence of this OID tells Windows XP that the cert is intended
+# for use by the computer itself, and not by an end-user.
+#
+# The other solution is to use Microsoft's web certificate server
+# to generate these certs.
+#
+# 1.3.6.1.4.1.311.17.2