Some distro (notably Ubuntu in this case) "cleverly" link some (not all) SSL-aware applications against the GnuTLS library rather than OpenSSL.
While this works fine most of the time, it creates some problems in those configuration files where you want to specify an explicit name for a cipher or cipher group. It turns out that the two libraries use different (and rigorously incompatible) names for the same ciphers. In this specific case, the problem was that a GnuTLS-OpenLDAP daemon was complaining at this (OpenSSL syntax) directive in slapd.conf:
TLSCipherSuite HIGH:MEDIUM:-SSLv2
HIGH, MEDIUM and SSLv2 are OpenSSL specific shortcut words to indicate a whole list of ciphers; GnuTLS apparently does not support them. Even if you were to replace them with the actual list of ciphers, that still wouldn't work because OpenSSL and GnuTLS use different names for the same ciphers.
You can see that by requesting the list of supported ciphers with the two tools:
# openssl ciphers -v DHE-RSA-AES256-SHA SSLv3 Kx=DH Au=RSA Enc=AES(256) Mac=SHA1 DHE-DSS-AES256-SHA SSLv3 Kx=DH Au=DSS Enc=AES(256) Mac=SHA1 AES256-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA1 ...
# gnutls-cli --list Cipher suites: TLS_ANON_DH_ARCFOUR_MD5 0x00, 0x18 SSL 3.0 TLS_ANON_DH_3DES_EDE_CBC_SHA1 0x00, 0x1b SSL 3.0 TLS_ANON_DH_AES_128_CBC_SHA1 0x00, 0x34 SSL 3.0 ... Certificate types: X.509, OPENPGP Protocols: SSL 3.0, TLS 1.0, TLS 1.1, TLS 1.2 Ciphers: AES 256 CBC, AES 128 CBC, 3DES 168 CBC, DES CBC, ARCFOUR 128, ARCFOUR 40, RC2 40, NULL MACs: SHA, MD5, SHA256, SHA384, SHA512, MD2, RIPEMD160, NULL Key exchange algorithms: Anon DH, RSA, RSA EXPORT, DHE RSA, DHE DSS, SRP DSS, SRP RSA, SRP, PSK, DHE PSK Compression: LZO, DEFLATE, NULL
More comprehensive lists can be found here (GnuTLS) and here (OpenSSL). Don't be deceived by the fact that GnuTLS names look similar to those used in the official specifications (those listed in the OpenSSL page); in reality they differ (and the names used by OpenSSL differ from both). For example, whereas the official cipher name is TLS_RSA_WITH_NULL_MD5, GnuTLS uses TLS_RSA_NULL_MD5, but OpenSSL uses NULL-MD5; or whereas the official name is TLS_DHE_RSA_WITH_AES_128_CBC_SHA, GnuTLS uses TLS_DHE_RSA_AES_128_CBC_SHA1, OpenSSL uses DHE-RSA-AES128-SHA; for the official TLS_DHE_DSS_WITH_RC4_128_SHA, GnuTLS offers TLS_DHE_DSS_ARCFOUR_SHA1 while OpenSSL uses DHE-DSS-RC4-SHA; and so on and so forth. Nor there seem to be a predictable way to derive one given one of the two other. Nice, uh?
GnuTLS only supports SSL 3.0 and later (TLS) algorithms, while OpenSSL still supports older SSL versions (in addition to SSL 3.0 and TLS, of course). However, for all practical purposes, SSL versions prior to 3.0 should not be used anyway. Also, to further complicate things, what OpenSSL calls "SSLv3" is effectively SSLv3 plus TLS (this is from the documentation: "...description of protocol version (SSLv2 or SSLv3; the latter includes TLS)").
After a bit of research, I realized that 99% of the configuration file documentation you can find on the Internet refers to SSL cipher and cipher group names using OpenSSL conventions (which kind of makes sense, given that that's what 99% of the distros do). However, it seems that no ready-made tool or table exists to easily convert between the two syntaxes, so I decided to hack my own.
Disclaimer: what follows is just something that "Works For Me (tm)". I make no guarantees that it will work for you, or even that it's correct at all.
After writing down all the names, and doing a painful comparison by hand of the crypto, hash, key exchange etc. algorithm used by each, I came up with the following (incomplete) equivalence table:
Standard name GnuTLS name OpenSSL name ---------------------------------------------------------------------------------- TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 TLS_RSA_EXPORT_ARCFOUR_40_MD5 EXP-ADH-RC4-MD5 TLS_DHE_DSS_WITH_RC4_128_SHA TLS_DHE_DSS_ARCFOUR_SHA1 DHE-DSS-RC4-SHA SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA TLS_DHE_DSS_3DES_EDE_CBC_SHA1 EDH-DSS-DES-CBC3-SHA SSL_DHE_RSA_WITH_DES_CBC_SHA TLS_DHE_RSA_3DES_EDE_CBC_SHA1 EDH-RSA-DES-CBC3-SHA TLS_DHE_DSS_WITH_AES_128_CBC_SHA TLS_DHE_DSS_AES_128_CBC_SHA1 DHE-DSS-AES128-SHA TLS_DHE_DSS_WITH_AES_256_CBC_SHA TLS_DHE_DSS_AES_256_CBC_SHA1 DHE-DSS-AES256-SHA TLS_DHE_RSA_WITH_AES_128_CBC_SHA TLS_DHE_RSA_AES_128_CBC_SHA1 DHE-RSA-AES128-SHA TLS_DHE_RSA_WITH_AES_256_CBC_SHA TLS_DHE_RSA_AES_256_CBC_SHA1 DHE-RSA-AES256-SHA TLS_RSA_WITH_AES_128_CBC_SHA TLS_RSA_AES_128_CBC_SHA1 AES128-SHA TLS_RSA_WITH_AES_256_CBC_SHA TLS_RSA_AES_256_CBC_SHA1 AES256-SHA TLS_RSA_WITH_RC4_128_MD5 TLS_RSA_ARCFOUR_MD5 RC4-MD5 TLS_RSA_WITH_RC4_128_SHA TLS_RSA_ARCFOUR_SHA1 RC4-SHA TLS_DH_anon_WITH_RC4_128_MD5 TLS_ANON_DH_ARCFOUR_MD5 ADH-RC4-MD5 TLS_DH_anon_WITH_3DES_EDE_CBC_SHA TLS_ANON_DH_3DES_EDE_CBC_SHA1 ADH-DES-CBC3-SHA TLS_RSA_WITH_NULL_MD5 TLS_RSA_NULL_MD5 NULL-MD5 TLS_DH_anon_WITH_AES_128_CBC_SHA TLS_ANON_DH_AES_128_CBC_SHA1 ADH-AES128-SHA TLS_DH_anon_WITH_AES_256_CBC_SHA TLS_ANON_DH_AES_256_CBC_SHA1 ADH-AES256-SHA TLS_RSA_WITH_3DES_EDE_CBC_SHA TLS_RSA_3DES_EDE_CBC_SHA1 DES-CBC3-SHA
There are some cipher suites only supported by OpenSSL (like the camellia-based ones), and some only supported by GnuTLS (for example the *_PSK_* or the *_SRP_* ones); those do not appear in the table above. While the table is incomplete, those that do appear seem to cover the majority of the commonly used ciphers.
Now, having that table is not enough yet; if you're migrating a config file in OpenSSL format that uses cipher suite strings, like HIGH, MEDIUM etc., you would first have to expand those to their component ciphers, and then find the GnuTLS equivalents. Now that we have the above table, this can be automated, and here is a simple script that will do that:
#!/bin/bash # ssl_cipher_convert: given a list of ciphersuites as understood by # OpenSSL, outputs the equivalent list on GnuTLS format. usage() { echo "Usage: $0 [ -h ] [ -t convtable ]" >&2 echo "" >&2 echo "Example: openssl ciphers -v 'HIGH:MEDIUM' | $0 -t /path/to/convtable.txt" >&2 } # default equivalence table location convtable=/home/waldner/convtable.txt while [ $# -gt 0 ]; do case "$1" in -h) usage exit 1 ;; -t) convtable=$2 shift 2 ;; *) echo "Unknown option $1" >&2 usage exit 1 ;; esac done # sanity checks if [ ! -f "$convtable" ]; then echo "Conversion table $convtable not found!" >&2 usage exit 1 fi # Do the conversion awk ' NR==FNR { # skip header lines if(FNR>2) eq[$3]=$2 next } { # nobody is perfect! print ($1 in eq)?eq[$1]:"Not in table!" } ' "$convtable" - exit 0
Let's test it:
$ openssl ciphers -v 'HIGH:MEDIUM:-SSLv2' | ssl_cipher_convert TLS_ANON_DH_AES_256_CBC_SHA1 TLS_DHE_RSA_AES_256_CBC_SHA1 TLS_DHE_DSS_AES_256_CBC_SHA1 TLS_RSA_AES_256_CBC_SHA1 TLS_ANON_DH_AES_128_CBC_SHA1 TLS_DHE_RSA_AES_128_CBC_SHA1 TLS_DHE_DSS_AES_128_CBC_SHA1 TLS_RSA_AES_128_CBC_SHA1 TLS_ANON_DH_3DES_EDE_CBC_SHA1 TLS_DHE_RSA_3DES_EDE_CBC_SHA1 TLS_DHE_DSS_3DES_EDE_CBC_SHA1 TLS_RSA_3DES_EDE_CBC_SHA1 TLS_ANON_DH_ARCFOUR_MD5 TLS_RSA_ARCFOUR_SHA1 TLS_RSA_ARCFOUR_MD5 $ openssl ciphers -v 'HIGH:-SSLv2:@STRENGTH' | ssl_cipher_convert TLS_ANON_DH_AES_256_CBC_SHA1 TLS_DHE_RSA_AES_256_CBC_SHA1 TLS_DHE_DSS_AES_256_CBC_SHA1 TLS_RSA_AES_256_CBC_SHA1 TLS_ANON_DH_3DES_EDE_CBC_SHA1 TLS_DHE_RSA_3DES_EDE_CBC_SHA1 TLS_DHE_DSS_3DES_EDE_CBC_SHA1 TLS_RSA_3DES_EDE_CBC_SHA1 TLS_ANON_DH_AES_128_CBC_SHA1 TLS_DHE_RSA_AES_128_CBC_SHA1 TLS_DHE_DSS_AES_128_CBC_SHA1 TLS_RSA_AES_128_CBC_SHA1 $ openssl ciphers -v '3DES:+RSA' | ssl_cipher_convert TLS_ANON_DH_3DES_EDE_CBC_SHA1 TLS_DHE_RSA_3DES_EDE_CBC_SHA1 TLS_DHE_DSS_3DES_EDE_CBC_SHA1 TLS_RSA_3DES_EDE_CBC_SHA1 Not in table! # nobody's perfect, as I said
And there you have it. Even if you happen to give it a cipher string that expands to some ciphers that are not in the table, I think it could still be useful for finding a common subset (ie, take only those that are found). For the problem at hand, GnuTLS-OpenLDAP requires the ciphers to be in a colon-separated string, so a little postprocessing is necessary:
$ openssl ciphers -v 'HIGH:MEDIUM:-SSLv2' | ssl_cipher_convert | awk '{printf "%s%s", s, $0;s=":"}END{print""}' TLS_ANON_DH_AES_256_CBC_SHA1:TLS_DHE_RSA_AES_256_CBC_SHA1:TLS_DHE_DSS_AES_256_CBC_SHA1:TLS_RSA_AES_256_CBC_SHA1:TLS_ANON_DH_AES_128_CBC_SHA1:TLS_DHE_RSA_AES_128_CBC_SHA1:TLS_DHE_DSS_AES_128_CBC_SHA1:TLS_RSA_AES_128_CBC_SHA1:TLS_ANON_DH_3DES_EDE_CBC_SHA1:TLS_DHE_RSA_3DES_EDE_CBC_SHA1:TLS_DHE_DSS_3DES_EDE_CBC_SHA1:TLS_RSA_3DES_EDE_CBC_SHA1:TLS_ANON_DH_ARCFOUR_MD5:TLS_RSA_ARCFOUR_SHA1:TLS_RSA_ARCFOUR_MD5
ready to be copied and pasted into the TLSCipherSuite directive in slapd.conf.
Hey,
I made a small script depending on openssl and gnutls-cli for automatic conversion from openssl to gnutls:
---
#!/bin/bash
OPENSSL_CIPHERS_DEFAULT="ALL:-ADH:-RC4+RSA:+HIGH:-MEDIUM:-LOW:-SSLv2:-EXP"
OPENSSL_CIPHERS=${1:-OPENSSL_CIPHERS_DEFAULT}
HEX_IDS=$(openssl ciphers -V 'ALL:-ADH:-RC4+RSA:+HIGH:-MEDIUM:-LOW:-SSLv2:-EXP' | awk '{print $1;}' | tr '[:upper:]' '[:lower:]')
GNUTLS_CIPHERS=""
for ID in ${HEX_IDS}; do
ID=$(echo $ID | sed 's/,/, /g')
GNUTLS=$(gnutls-cli --list | grep "$ID" | awk '{print $1;}')
if test "${GNUTLS}" = ""; then
echo "Unsupported: $ID"
else
GNUTLS_CIPHERS="${GNUTLS_CIPHERS}:${GNUTLS}"
fi
done
echo "OpenSSL-Ciphers: ${OPENSSL_CIPHERS}"
echo "GnuTLS-Ciphers: ${GNUTLS_CIPHERS}"
---
Hey, this is really nice and helpful! Thanks!
I've taken the liberty of making some minor changes to the script. Here it is.