OpenVPN and remote-cert-tls server

This required a bit of digging into OpenVPN’s and OpenSSL’s code to figure out.

The problem

This error:

Thu Sep 11 00:12:05 2014 Validating certificate key usage
Thu Sep 11 00:12:05 2014 ++ Certificate has key usage  00f8, expects 00a0
Thu Sep 11 00:12:05 2014 ++ Certificate has key usage  00f8, expects 0088

The condition

Using openvpn with the following option:

remote-cert-tls server

The solution

(for me) to add this to openvpn’s config file:

remote-cert-ku f8

The explanation


remote-cert-tls attempts to solve one problem: Lets say you run a CA and you distribute the certificates to 2 people including me and you. Then you setup a VPN server for us to use and you generate another certificate for the VPN server.

As always, the problem that certificates attempt to solve is “how do you know you’re connecting to the remote end you assume you are. In normal SSL pages you trust a CA to verify that the CN of the certificate matches the owner of the domain. If you want to achieve the same thing with openvpn then you need to verify the CN of the remote end against either the hostname or a predefined string. If not then you need to use the “remote-cert-tls server” option.

If you don’t use any of the above methods then I can fire up an openvpn server using the certificate you provided me with and since both my certificate and the actual VPN server’s certificate are signed by the same CA you would be verifying both and be equally willing to connect to both, thus allowing me to spy on you.

To solve this kind of problems X509 has some properties for certificates that designate them for certain purposes. E.g. one of them is to run a VPN endpoint (TLS Web Client Authentication)

To be precise there are 2+1 such designations in X509:

X509v3 Key Usage: 
    Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment, Key Agreement
X509v3 Extended Key Usage: 
    TLS Web Server Authentication, TLS Web Client Authentication, IPSec End System, IPSec Tunnel, Time Stamping
Netscape Cert Type: 
    SSL Client, SSL Server, S/MIME, Object Signing

“Netscape Cert Type” is kind of old. “Key Usage” is the main one and “Extended Key Usage” is the final addition. Ignoring NS Cert Type, “Key Usage” is a bitmap and thus has limited space for expansion. “Extended Key Usage” on the other hand is a list of object identifiers which allows for unlimited expansion.

The certificate

The certificate I was using for the server-side of the OpenVPN had the above attributes. Ignoring NS Cert Type once more, the other two correspond to the following data:

  494:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 Key Usage
  499:d=5  hl=2 l=   4 prim: OCTET STRING      [HEX DUMP]:030203F8
  433:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 Extended Key Usage
  438:d=5  hl=2 l=  52 prim: OCTET STRING      [HEX DUMP]:303206082B0601050507030106082B0601050507030206082B0601050507030506082B0601050507030606082B06010505070308

Starting with “Key Usage”, the actual value is “F8”. The meaning of each bit can be found in OpenSSL’s code:

#define KU_DIGITAL_SIGNATURE    0x0080
#define KU_NON_REPUDIATION      0x0040
#define KU_KEY_ENCIPHERMENT     0x0020
#define KU_DATA_ENCIPHERMENT    0x0010
#define KU_KEY_AGREEMENT        0x0008
#define KU_KEY_CERT_SIGN        0x0004
#define KU_CRL_SIGN             0x0002
#define KU_ENCIPHER_ONLY        0x0001
#define KU_DECIPHER_ONLY        0x8000

On the other hand, the “Extended Key Usage” part contains the following Object IDs:

06082B 06010505070301 -> serverAuth (TLS Web Server Authentication)
06082B 06010505070302 -> clientAuth (TLS Web Client Authentication)
06082B 06010505070305 -> ipsecEndSystem (IPSec End System)
06082B 06010505070306 -> ipsecTunnel (IPSec Tunnel)
06082B 06010505070308 -> timeStamping (Time Stamping)

The “bug”

The first thing to notice is that the failure is for “Key Usage” and not for “Extended Key Usage” (took me some time to figure out).

After that, a bit of digging into the code confirms that OpenVPN attempts to verify a bitmap with equality. I.e. it gets the certificates’ value and compares it against a predefined list of allowed values, which according to OpenVPN’s documentation defaults to “a0 88” (which means one of them). However the actual certificates bitmap value is 0xf8 as mentioned above. And thus the comparison fails with the error:

Thu Sep 11 00:12:05 2014 Validating certificate key usage
Thu Sep 11 00:12:05 2014 ++ Certificate has key usage  00f8, expects 00a0
Thu Sep 11 00:12:05 2014 ++ Certificate has key usage  00f8, expects 0088

The reason I’m calling this a bug is because it’s not sensible to use equality to compare against a bitmap. Instead one can use AND, in which case there would be:

( <certificate's value> & <desired value> ) == <desired value>
( 0xf8 & 0xa0) == 0xa0  -> True

In order for the validation to succeed with the defaults the certificate should have one of the following designations:

0xa0: Digital Signature, Key Encipherment
0x88: Digital Signature, Key Agreement

The solution

So there, since the comparison is done with equality you can do one of the following:

  • Use the above Key Usage on the certificate (inconvenient)
  • Don’t use “remote-cert-tls server” (bad)
  • Use “remote-cert-ku XX” where XX is the value of your certificate which can be seen in OpenVPN’s messages (the last octet). In my case it’s f8.



  1. remote-cert-ku shouldn’t be used as it would still allow for a MITM, as any certificate, server or client, can be configured with those 5 keyUsages – remote-cert-eku should be used instead.

    Server config should have: remote-cert-eku “TLS Web Client Authentication”
    openssl.cnf v3 profile: extendedKeyUsage = critical, serverAuth

    Client config should have: remote-cert-eku “TLS Web Server Authentication”
    openssl.cnf v3 profile: extendedKeyUsage = critical, clientAuth

    They’re opposite of one another in the openvpn configs as it’s a designation of what each expects the remote endpoint to be, i.e. server expects remote endpoint to be a client and vice versa.

    OpenVPN provides a decent explanation on one of their help pages (Configuring encryption – half way down):

    • According to the man page:

      –remote-cert-tls client|server
      Require that peer certificate was signed with an explicit key
      usage and extended key usage based on RFC3280 TLS rules.

      Notice the “and” part which means that both ku and eku are checked. That’s in fact the reason I made this post.

      • The problem is if you properly issue a vpn server cert with eku serverAuth and client cert with eku clientAuth, the connection will fail authentication with –remote-cert-tls client/server.

        Simply specifying ‘remote-cert-ku f8’ means the remote cert must have the following 5 ku’s set: nonRepudiation, digitalSignature, keyEncipherment, keyAgreement, dataEncipherment. If one doesn’t have properly issued client/server certs (i.e. ones issued with eku serverAuth/clientAuth), then they would need to use remote-cert-tls server/client in the client config to tell OpenVPN that it needs to use the configs themselves to differentiate a client from a server, as the certificates themselves do not… something I find completely bonkers, as the certificate itself should be relied upon to determine it’s usage, not a config.

        This problem exists in large part to to OpenVPN’s attachement to Easy-RSA, of which does not create certificates with eku clientAuth/serverAuth, or any eku for that matter.

        • I should have specified I’m not knocking your post… I was extremely grateful to come across it, as your blog post was the only one I came across after hours of research, trying to figure out why authentication kept failing with properly created certs.

          My only point is if one utilizes remote-cert-ku f8, non-server certs can have the 5 ku’s set, which equates to f8; whereas only server certs should have eku serverAuth. If eku serverAuth is utilized, the client config would then gain remote-cert-eku ‘TLS Web Server Authentication’ versus remote-cert-ku f8, since keyUsage f8 doesn’t identify the remote cert as a server… it identifies as a certificate that simply has ku’s: nonRepudiation, digitalSignature, keyEncipherment, keyAgreement, dataEncipherment.

  2. Hello
    Just in case you wonder, remote-cert-tls server is set in src/openvpn/options.c:
    rmote_cert_ku = 0xa0 + rmote_cert_ku = 0x88 + remote_cert_eku = “TLS Web Server Authentication”

    This is in the code ( src/openvpn/options.c):
    if (streq (p[1], “server”))
    options->remote_cert_ku[0] = 0xa0;
    options->remote_cert_ku[1] = 0x88;
    options->remote_cert_eku = “TLS Web Server Authentication”;

    I would heavely recomend to check the remote_cert_eku. If you are not happy with the ku, just set both manually e.g.:
    remote-cert-eku “TLS Web Server Authentica-tion”
    remote-cert-ku f8 // Or whatever you need her


Leave a Reply

Your email address will not be published. Required fields are marked *