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
Background
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> or: ( 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.
Thanks for figuring this out so I didn’t have to dig this deep into the source.
LikeLike
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): https://community.openvpn.net/openvpn/wiki/GettingStartedwithOVPN
LikeLike
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.
LikeLike
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.
LikeLike
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.
LikeLike
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”
and
remote-cert-ku f8 // Or whatever you need her
Cheers,
Rafael
LikeLike