stefan's blag and stuff

Blog – 2015-05-01 – Choosing Diffie Hellman Key Exchange Parameters

This advice is outdated! Don't use it. See my newer blog post.

In an older blog post I explained how to fix a specific courier imap error message and set up a cron job to generate Diffie Hellman Key Exchange Parameters.

Now I found out that following the manpages of courier blindly was not the right choice security wise.

The Key Exchange Parameters are too weak

Everything started with a friend's email yesterday complaining that his openbsd's offlineimap installation cannot connect to our server. He provided the console output of offlineimap:

$ offlineimap
OfflineIMAP 6.5.4
  Licensed under the GNU GPL v2+ (v2 or any later version)
Account sync stcim:
 *** Processing account stcim
 Establishing connection to stcim.de:993
 ERROR: Unknown SSL protocol connecting to host 'stcim.de' forrepository 'remote-stcim'. OpenSSL responded:
[Errno 1] _ssl.c:510: error:1408D06E:SSL routines:SSL3_GET_KEY_EXCHANGE:bad dh p length

A quick search brings up the (maybe) offending sourcecode and commit message:

	Reject DH keys sent by a server if they are considered too small; inspired
	by a similar BoringSSL change, but raising the limit to 1024 bits.
	ok jsing@ markus@ guenther@ deraadt@
[…]

	/*
	 * Check the strength of the DH key just constructed.
	 * Discard keys weaker than 1024 bits.
	 */

	if (DH_size(dh) < 1024 / 8) {
		SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE,
		    SSL_R_BAD_DH_P_LENGTH);
		goto err;
	}

And looking at the mkdhparams manpage shows

$ man mkdhparams
ENVIRONMENT VARIABLES
       BITS
                  Customize the DH parameter bit size. The default value
                  depends on whether this script uses OpenSSL or GnuTLS
                  libraries. For OpenSSL the default number of bits is 768.
                  GnuTLS uses a security level setting, rather than the number
                  of bits, and the default security level is "high".

and checking the file /etc/courier/dhparams.pem:

$ openssl dhparam -text -in /etc/courier/dhparams.pem -noout
    PKCS#3 DH Parameters: (768 bit)
        prime:
             […]
        generator: 2 (0x2)

Ups. Our Diffie Hellman Key Exchange Parameters are too weak, only 768 bits, but luckily openbsd has found it.

Using a static 4096 bit prime

A good starting point for security related administration is the Applied Crypto Hardening PDF from bettercrypto.org. It's a comprehensive guide with lots of snippets, explanations and links to other resources.

Notes about the Diffie Hellman Parameters are in section Section 3.7 "A Note on Diffie Hellman Key Exchange":

A common question is which Diffie Hellman (DH) Parameters should be used for Diffie Hellman key exchanges. We follow the recommendations in ECRYPT II [IS12, chapter 16]
and it links to a stackexchange question How large should a Diffie-Hellman p be? and to the RFC 3526.

Ok, equipped with our new knowledge (using a well chosen prime with more than 1024 bits) we fix our server setup. First we get the group with id 16 from RFC 3526 and compare the prime in the file with the prime in the RFC document manually.

$ wget https://bettercrypto.org/static/dhparams/group16.pem
$ sha256sum group16.pem
a6e3c01dabf4fe5cb32b20e1f84e55a2aa4309159e102867a1ca8fa7e8acd991  group16.pem
$ openssl dhparam -text -noout -in group16.pem
    PKCS#3 DH Parameters: (4096 bit)
        prime:
            00:ff:ff:ff:ff:ff:ff:ff:ff:c9:0f:da:a2:21:68:
            c2:34:c4:c6:62:8b:80:dc:1c:d1:29:02:4e:08:8a:
            67:cc:74:02:0b:be:a6:3b:13:9b:22:51:4a:08:79:
            8e:34:04:dd:ef:95:19:b3:cd:3a:43:1b:30:2b:0a:
            6d:f2:5f:14:37:4f:e1:35:6d:6d:51:c2:45:e4:85:
            b5:76:62:5e:7e:c6:f4:4c:42:e9:a6:37:ed:6b:0b:
            ff:5c:b6:f4:06:b7:ed:ee:38:6b:fb:5a:89:9f:a5:
            ae:9f:24:11:7c:4b:1f:e6:49:28:66:51:ec:e4:5b:
            3d:c2:00:7c:b8:a1:63:bf:05:98:da:48:36:1c:55:
            d3:9a:69:16:3f:a8:fd:24:cf:5f:83:65:5d:23:dc:
            a3:ad:96:1c:62:f3:56:20:85:52:bb:9e:d5:29:07:
            70:96:96:6d:67:0c:35:4e:4a:bc:98:04:f1:74:6c:
            08:ca:18:21:7c:32:90:5e:46:2e:36:ce:3b:e3:9e:
            77:2c:18:0e:86:03:9b:27:83:a2:ec:07:a2:8f:b5:
            c5:5d:f0:6f:4c:52:c9:de:2b:cb:f6:95:58:17:18:
            39:95:49:7c:ea:95:6a:e5:15:d2:26:18:98:fa:05:
            10:15:72:8e:5a:8a:aa:c4:2d:ad:33:17:0d:04:50:
            7a:33:a8:55:21:ab:df:1c:ba:64:ec:fb:85:04:58:
            db:ef:0a:8a:ea:71:57:5d:06:0c:7d:b3:97:0f:85:
            a6:e1:e4:c7:ab:f5:ae:8c:db:09:33:d7:1e:8c:94:
            e0:4a:25:61:9d:ce:e3:d2:26:1a:d2:ee:6b:f1:2f:
            fa:06:d9:8a:08:64:d8:76:02:73:3e:c8:6a:64:52:
            1f:2b:18:17:7b:20:0c:bb:e1:17:57:7a:61:5d:6c:
            77:09:88:c0:ba:d9:46:e2:08:e2:4f:a0:74:e5:ab:
            31:43:db:5b:fc:e0:fd:10:8e:4b:82:d1:20:a9:21:
            08:01:1a:72:3c:12:a7:87:e6:d7:88:71:9a:10:bd:
            ba:5b:26:99:c3:27:18:6a:f4:e2:3c:1a:94:68:34:
            b6:15:0b:da:25:83:e9:ca:2a:d4:4c:e8:db:bb:c2:
            db:04:de:8e:f9:2e:8e:fc:14:1f:be:ca:a6:28:7c:
            59:47:4e:6b:c0:5d:99:b2:96:4f:a0:90:c3:a2:23:
            3b:a1:86:51:5b:e7:ed:1f:61:29:70:ce:e2:d7:af:
            b8:1b:dd:76:21:70:48:1c:d0:06:91:27:d5:b0:5a:
            a9:93:b4:ea:98:8d:8f:dd:c1:86:ff:b7:dc:90:a6:
            c0:8f:4d:f4:35:c9:34:06:31:99:ff:ff:ff:ff:ff:
            ff:ff:ff
        generator: 2 (0x2)

Now we remove the cron job and move the new Key Exchange Parameters into place, optionally restarting the courier deamon.

$ rm /etc/cron.monthly/mkdhparams
$ mv group16.pem /etc/courier/dhparams.pem
$ systemctl restart courier-imap-ssl.service

Openssl 1.0.2 shows the Diffie Hellman Parameters

It's always a good idea to check whether such a change actually works. To inspect ssl/tls connections openssl has the s_client command which spins up a connection to a server (including support for starttls) and shows various informations about chipers and certificates. Sadly only the recent version 1.0.2 shows the Diffie Hellman Parameters in plain text according to the stackexchange question OpenSSL: Display DH Parameters. For earlier versions you have to parse the size and prime from the tls message in hex yourself. So after upgrading to the newest version you can execute:

$ openssl version
OpenSSL 1.0.2a 19 Mar 2015
$ openssl s_client -host stcim.de -port 993
[…]
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: DH, 4096 bits
---

Done! The last thing I want to say: Simon, can your offlineimap connect now?