Set up Certificate Authentication in OpenConnect VPN Server (ocserv)
This tutorial will be showing you how to set up certificate authentication in OpenConnect VPN server (ocserv) on Debian/Ubuntu/CentOS/RHEL. OpenConnect (ocserv) is an open-source implementation of the Cisco AnyConnect VPN protocol.
In a previous article, I explained the steps to set up OpenConnect VPN server with Let’s Encrypt TLS server certificate. Let’s Encrypt does not issue client certificate, so in that article, we used password authentication. Entering username and password every time can be a hassle, especially if the client software, such as the Cisco AnyConnect app on iOS, doesn’t offer an option to remember passwords. Many OpenConnect client software can import user certificates, which will free the user from entering username and password. Certificate authentication is also more secure than password authentication.
Requirements
To follow this tutorial, it’s assumed that you have already set up an OpenConnect VPN server with Let’s Encrypt TLS server certificate. If not, please follow one of the following tutorials.
- Set up OpenConnect VPN Server (ocserv) on Ubuntu 20.04 with Let’s Encrypt
- Set Up OpenConnect VPN Server (ocserv) on Debian 11 Bullseye with Let’s Encrypt
- Set Up OpenConnect VPN Server (ocserv) on CentOS 8/RHEL 8 with Let’s Encrypt
We will set up our own CA (Certificate Authority) to sign client certificate. The ocserv
daemon should continue using the TLS server certificate issued by Let’s Encrypt, so client software won’t display security warning.
Setting up Your Own CA (Certificate Authority)
We want to use certificate authentication, but Let’s Encrypt does not issue client certificate, so we need to create our own CA. You can use openssl
to do the job, but ocserv recommends GnuTLS, so I will show you how to use GnuTLS.
Install gnutls-bin
package on Debian/Ubuntu server.
sudo apt install gnutls-bin
Install gnutls-utils
package on CentOS/RHEL.
sudo dnf install gnutls-utils
Create a sub-directory in /etc/ocserv/
to hold private keys and certificates.
sudo mkdir /etc/ocserv/ssl/
Change your working directory.
cd /etc/ocserv/ssl/
Generate a private key for the CA with the certtool
command, which is provided by the gnutls-bin
or gnutls-utils
package. By default, it generates a 3072 bit RSA key, which is sufficient.
sudo certtool --generate-privkey --outfile ca-privkey.pem
Before generating the CA certificate, let’s create the CA certificate template file. The template file format can be found in certtool manual (man certtool
).
sudo nano ca-cert.cfg
Add the following lines to the file. Replace placeholders with the appropriate values.
# X.509 Certificate options # The organization of the subject. organization = "vpn.example.com" # The common name of the certificate owner. cn = "Example CA" # The serial number of the certificate. serial = 001 # In how many days, counting from today, this certificate will expire. Use -1 if there is no expiration date. expiration_days = -1 # Whether this is a CA certificate or not ca # Whether this certificate will be used to sign data signing_key # Whether this key will be used to sign other certificates. cert_signing_key # Whether this key will be used to sign CRLs. crl_signing_key
Save and close the file. Now generate the CA certificate using configurations from the template file.
sudo certtool --generate-self-signed --load-privkey ca-privkey.pem --template ca-cert.cfg --outfile ca-cert.pem
Now we have a CA certificate file (ca-cert.pem
).
Generating Client Certificate
Now run the following command to generate client private key.
sudo certtool --generate-privkey --outfile client-privkey.pem
Create the client certificate template file.
sudo nano client-cert.cfg
Add the following lines into the file. The uid must be a username in the /etc/ocserv/ocpasswd
file.
# X.509 Certificate options # The organization of the subject. organization = "vpn.example.com" # The common name of the certificate owner. cn = "John Doe" # A user id of the certificate owner. uid = "username" # In how many days, counting from today, this certificate will expire. Use -1 if there is no expiration date. expiration_days = 3650 # Whether this certificate will be used for a TLS server tls_www_client # Whether this certificate will be used to sign data signing_key # Whether this certificate will be used to encrypt data (needed # in TLS RSA ciphersuites). Note that it is preferred to use different # keys for encryption and signing. encryption_key
Save and close the file. Then run the following command to generate client certificate, which will be signed by the CA private key.
sudo certtool --generate-certificate --load-privkey client-privkey.pem --load-ca-certificate ca-cert.pem --load-ca-privkey ca-privkey.pem --template client-cert.cfg --outfile client-cert.pem
Combine the client private key and certificate in a PKCS #12 file that is protected by a PIN.
sudo certtool --to-p12 --load-privkey client-privkey.pem --load-certificate client-cert.pem --pkcs-cipher aes-256 --outfile client.p12 --outder
Now we have the client private key and certificate combined into one file client.p12
.
Note that the Ciso AnyConnect app on iOS doesn’t support AES-256 cipher. It will refuse to import the client certificate. If the user is using iOS device, then you can choose the 3des-pkcs12
cipher.
sudo certtool --to-p12 --load-privkey client-privkey.pem --load-certificate client-cert.pem --pkcs-cipher 3des-pkcs12 --outfile ios-client.p12 --outder
The client private key and certificate combined into one file ios-client.p12
.
Certificate Signing Request
This step is only needed if there are multiple VPN users, and the user wants to use his/her own private key.
In order to keep end users’ private keys secret, users can generate certificate signing request (CSR) with their own private keys, then send certificate requests to admin, who then issues client certificates to users. First, they have to generate private key and the client certificate template using the commands mentioned above. Then generate a CSR with the following command. The request.pem
file is signed by user’s private key.
certtool --generate-request --load-privkey client-privkey.pem --template client-cert.cfg --outfile request.pem
Next, the user sends the request.pem
and client-cert.cfg
file to admin, who runs the following command to generate client certificate.
sudo certtool --generate-certificate --load-ca-certificate ca-cert.pem --load-ca-privkey ca-privkey.pem --load-request request.pem --template client-cert.cfg --outfile client-cert.pem
After that, the admin sends client-cert.pem
certificate file to the user.
Enabling Certificate Authentication in ocserv Daemon
Edit ocserv configuration file.
sudo nano /etc/ocserv/ocserv.conf
In the previous tutorial, we added the following line to enable password authentication.
auth = "plain[passwd=/etc/ocserv/ocpasswd]"
To enable certificate authentication, uncomment the following line.
auth = "certificate"
If the above two lines are both uncommented, that means the user must pass both password authentication and certificate authentication. So if certificate authentication is enough to prove identity, then comment out the first line.
If you allow users to chose either certificate authentication or password authentication, then you should have the following lines instead.
enable-auth = "plain[passwd=/etc/ocserv/ocpasswd]" auth = "certificate"
Now find the ca-cert parameter. On Debian/Ubuntu, it’s set to
ca-cert = /etc/ssl/certs/ssl-cert-snakeoil.pem
On CentOS 8/RHEL 8, it’s set to
ca-cert = /etc/ocserv/ca.pem
We need to use our own CA certificate to verify client certificate, so change this line to
ca-cert = /etc/ocserv/ssl/ca-cert.pem
Next, find the following line.
cert-user-oid = 0.9.2342.19200300.100.1.1
You don’t need to change it. I just want to tell you that 0.9.2342.19200300.100.1.1
represents the UID filed in client certificate. The above line tells ocserv
daemon to find the username from the UID field of client certificate. If the client certificate is successfully verified by the CA certificate and ocserv
daemon can find a matching username in /etc/ocserv/ocpasswd
file, then the client can login.
Save and close the file. Then restart ocserv.
sudo systemctl restart ocserv
Using Certificate Authentication on Debian/Ubuntu/CentOS/RHEL Desktop
Use the scp
command to download the client.p12
file to your Debian/Ubuntu/CentOS/RHEL desktop.
scp [email protected]:/etc/ocserv/ssl/client.p12 ~
Then install the openconnect
client software.
Debian/Ubuntu:
sudo apt install openconnect
CentOS/RHEL:
sudo dnf install epel-release sudo dnf install openconnect
To use certificate authentication, run
sudo openconnect -b vpn.example.com -c client.p12
You will be asked to unlock client private key with the passphrase you set ealier in this tutorial.
If the passphrase is entered correctly, you should now be connected to VPN server.
Using Certificate Authentication on Windows and MacOS Desktop
Download OpenConnect GUI client for Window or MacOS from OpenConnect GUI Github Page. Then create a new VPN connection profile and import the PKCS #12 file to user certificate field. Click the Save button. You will need to enter the PIN to unlock the private key. Once imported, you don’t have to enter username and password anymore.
Using Certificate Authentication on iOS Device
iOS users can use the Cisco AnyConnect app. To import client certificate in AnyConnect app, you can first send the PKCS #12 file to your email address in an attachment. Then open the mail app on iOS. Tap the attachment a few seconds and share it with AnyConnect. Then enter the PIN to import the file.
Once it’s imported, edit your VPN connection in AnyConnect. Go to Advanced
-> Certificate
and select the client certificate. Save your settings.
Now you don’t have to enter username and password anymore on your iOS device. The Cisco AnyConnect app doesn’t remember username and password, so in password authentication mode, VPN connection will drop when the phone is not in use. In certificate authentication mode, the app will automatically reconnect to VPN server if connection is dropped.
Issues with AnyConnect Client on iOS
The latest version of AnyConnect client on iOS has a problem when using certificate authentication in TLS 1.3 protocol. If you see the following error in the ocserv log (sudo journalctl -eu ocserv
), you have this same problem.
GnuTLS error (at worker-vpn.c:795): A TLS fatal alert has been received.
Either you need to use password authentication in the AnyConnect iOS client or disable TLS 1.3 in ocserv configuration file. To disable TLS1.3, find the tls-priorities
parameter in the /etc/ocserv/ocserv.conf
file, and add :-VERS-TLS1.3
at the end to disable TLS 1.3.
tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.3"
Save and close the file. Then restart ocserv.
sudo systemctl restart ocserv
Note: If you see the SSL 3.3
phrase in the ocserv logs, don’t panic. SSL 3.3 is another word for TLS 1.2. You are using a secure TLS connection.
Wrapping Up
I hope this tutorial helped you set up certificate authentication in OpenConnect VPN server. As always, if you found this post useful, then subscribe to our free newsletter to get more tips and tricks. Take care 🙂
Great! thank you!
You are welcome 🙂
Can I create my own certificate authority like DigiCert for example?
Creating your own certificate authority (CA) has already been explained in this article.
Great article!
is that possible use client certificate + google-authenticator for Two Factor Authentication?
user is using iOS device
@xiao
Imagine, if one day I want to revoke a certificate which I signed with the CA.
What should I do?
Is there any CRL mechanism in place?
Dear Team,
Its a great and very clear guide to use this option. i would like to know about how to revoke the client certificate from OCServ.
Trying to get a Chromebook import the p12 certificate. Getting invalid or corrupt file.
I’m stuck at PCKS#12 passphrase. I don’t know where I have set my passphrase in this tutorial
my bad for above problem. Now I got this error:Failed to read from SSL socket: The transmitted packet is too large(EMSGSIE). Failed to recv DPD request(1434)
Thank you very much for this tutorial – it’s really great! One thing which would be great to cover in this topic is the 2fa for ocserv – for example, with LinOTP. I would appreciate a lot if You could make an article about it 🙂
Thanks for this tutorial!
@Eugeniusz, you can find 2FA info here: https://gitlab.com/openconnect/recipes/-/blob/master/ocserv-2fa.md
you are super awesome! May God bless you!
Thanks for the detail article. I have followed along the instructions but I can’t add the certificate to both iPhone and Android device. The prompt for entering passphrase to import certificate keep popping up after I have enter the passphrase, and the certificate is never imported. I can’t figure out the reason for that…
2023-01-28 17:55 -05:00:
I encountered “password” errors when attempting to import the pkcs12-formatted file, generated as instructed here, into the Cisco AnyConnect app on both iOS and Android mobile devices. The information below describes how I solved this issue.
This issue occurred using the following server and client mobile devices:
Upon attempting to import the pkcs12-formatted file generated per the instructions here, the following error message is returned by the AnyConnect app:
On iPhone:
On Android:
Using the following options when generating the pkcs12-formatted file, for BOTH iOS and Android devices, allows the certificate to be successfully imported into AnyConnect on both devices:
Full command:
The instructions may need to be updated on this page and on other instruction pages on this site, if applicable.
Help courtesy of:
Stack Overflow: Installing pcks12 certificate in android “wrong password” bug thread.
Yes! You are right!
Thanks, TXCat. Hopefully the guide gets updated to reflect that.
P.S. Took me a minute to figure out where to import the certificate on the AnyConnect app. You have to tap “Advanced Preferences” then “Certificate” then the “Import” button at the bottom of the screen.
Thank you for commenting this solution
Hi,
Firstly, thanks for the article it was very helpful.
All works well for me when using a Windows client to connect either via username and password, or via the Client certificate authentication.
However, I have a printer that can accept VPN connections but it requires the output certificate file to be a .pfx rather than the .p12 as created with the commands above. I’ve tried various methods to convert the .p12 to a .pfx but without success, anyone that could help point me in the right direction it would be much appreaciated!
Thanks in advance
Adam
Just to automate certs little bit. Tested with Ubuntu 22.
#!/bin/bash
# Request client_id for file naming
read -p “Enter client_id: ” client_id
# Ask if the certificate is for iPhone
read -p “Is this certificate for an iPhone (yes/no)? ” is_iphone
# Step 2: Generate client private key
certtool –generate-privkey –outfile “${client_id}-client-privkey.pem”
# Step 4: Generate the client certificate
# Using process substitution to provide the template directly
certtool –generate-certificate \
–load-privkey “${client_id}-client-privkey.pem” \
–load-ca-certificate ca-cert.pem \
–load-ca-privkey ca-privkey.pem \
–template <(echo "organization = \"Org_Name\"
cn = \"${client_id}\"
uid = \"${client_id}\"
expiration_days = 3650
tls_www_client
signing_key
encryption_key") \
–outfile "${client_id}-client-cert.pem"
# Step 5: Convert the certificate and key to PKCS#12 format
unix_timestamp=$(date +%s)
# Choose cipher and hash based on the device
if [ "$is_iphone" = "yes" ]; then
pkcs_cipher="3des-pkcs12"
hash_algo="–hash SHA1"
else
pkcs_cipher="aes-256"
hash_algo=""
fi
certtool –to-p12 \
–load-privkey "${client_id}-client-privkey.pem" \
–load-certificate "${client_id}-client-cert.pem" \
–p12-name "${client_id}-${unix_timestamp}" \
–pkcs-cipher "$pkcs_cipher" \
$hash_algo \
Thank you so much for clear instructions
but I have a problem for using client certificate on an android device with openconnect app installed ( version 1.33 – Android 13 )
can you help me with this?
I use XCA windows version (latest version: 2.6.0) as client certificates generation tool . It uses OpenSSL 3.1.5 30 Jan 2024. client is openconnect-gui 1.6.0.
But it does not work with camouflage (http://ocserv.domain/?secretkey).
ocserv[342399]: worker: client certificate verification succeeded
Secret not found in URL, declining…
but It works with client certificates without camouflage and with camouflage and enable-auth = “plain[passwd=/etc/ocserv/ocpasswd]”
also openconnect-gui works with certtool client certificates.
Can you explain why openconnect server does’nt work with camouflage and certificates generated by XCA
Thanks for excellent manual! Does anybody know how to import keypair in macOS to use with Cisco AnyConnect Secure Mobility Client?