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.