package org.dcache.util; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import java.util.HashSet; import java.util.List; import java.util.Set; import static java.util.Arrays.asList; /** * Various useful cryptographic utility method */ public class Crypto { public enum CipherFlag { DISABLE_EC, DISABLE_RC4 } private static final Splitter CIPHER_FLAG_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults(); private Crypto() { } /* The following is a list of cipher suites that are problematic * with currently suported versions of Java. * * The problem is described here: * * https://bugs.launchpad.net/ubuntu/+source/openjdk-6/+bug/1006776 * * Note that, despite the comment in the ticket, the problem is * also present for OpenJDK v7. * * During the SSL/TLS handshake, the client will send a list of * supported ciphers. The server will choose one, based on the * client-supplied list and the ciphers it supports. * * The problem is that libnss3 supports only 3 EC (elliptic curve) * ciphers, but the Java security provider that wraps libnss3 * believes it supports all elliptic curve ciphers. If the * client's list of supported ciphers includes those based on EC * then the Java server may choose an EC cipher. This SSL * negotiation will then fail with the 'CKR_DOMAIN_PARAMS_INVALID' * error. For example, JGlobus will log the following: * * 19 Apr 2013 17:40:43 (SRM-zitpcx6184) [] * org.globus.common.Chained IOException: Authentication failed * [Caused by: Failure unspecified at GSS-API level [Caused by: * Failure unspecified at GSS-API level [Caused by: * sun.security.pkcs11.wrapper.PKCS11Exception: * CKR_DOMAIN_PARAMS_INVALID]]] * * Clients that do not support EC-based ciphers are not affected * by this bug; for example, OpenSSL prior to v1.0.0 (or there * abouts) and most Java-based clients. * * Some clients provide control of the cipher selection. For * example, the OpenSSL sample client ('s_client') provides the * -cipher option. This may be used to omit all eliptic curve * ciphers as a client-side work-around. For example, the * following command may be used to connect to the localhost's * HTTPS SRM endpoint while excluding any EC-based cipher: * * openssl s_client -connect localhost:8445 -cipher 'DEFAULT:!ECDH' * * It is also possible to disable support on the server by editing * the 'java.security' file to remove the security provider that * is supplying the (broken) eliptic curve support. This is * controlled by the file 'java.security', which is located in a * distribution-specific directory. For Debian, the file is in * the /etc/java-7-openjdk/security directory and for RHEL, it's * in the /usr/java/<version>/jre/lib/security directory. * * As we can't control all clients and the instructions for how to * disable the EC ciphers is fiddly, we have a server-side * work-around: dCache components (typically doors) can remove * these ciphers from the list of supported ciphers, preventing * them from being chosen during the SSL/TLS negotiation. * * The following list was generated from OpenJDK source code using * the command: */ // sed -n '/add.*TLS_ECDHE/s/.*add(\([^,]*\).*/ \1,/p' // sun/security/ssl/CipherSuite.java | sort public static final String[] EC_CIPHERS = { "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDHE_RSA_WITH_RC4_128_SHA", "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_NULL_SHA", "TLS_ECDHE_RSA_WITH_NULL_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_PSK_WITH_RC4_128_SHA", "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_PSK_WITH_NULL_SHA", "TLS_ECDHE_PSK_WITH_NULL_SHA256", "TLS_ECDHE_PSK_WITH_NULL_SHA384" }; /** * A list of Ciphers that make use of the RC4 (Rivest Cipher 4) stream * cipher. RC4 has several potential attacks and general recommendation * seems to disable RC4 whenever possible, as presented in RFC 7465. * * This list was generated with the following command: * * sed -n 's%^.*add( *"\([^"]*_RC4_[^"]*\)".*% "\1",%p' sun/security/ssl/CipherSuite.java|sort */ public static final String[] RC4_CIPHERS = { "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", "SSL_DH_anon_WITH_RC4_128_MD5", "SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA", "SSL_DHE_DSS_WITH_RC4_128_SHA", "SSL_RSA_EXPORT1024_WITH_RC4_56_SHA", "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_RSA_WITH_RC4_128_MD5", "SSL_RSA_WITH_RC4_128_SHA", "TLS_DHE_PSK_WITH_RC4_128_SHA", "TLS_ECDH_anon_WITH_RC4_128_SHA", "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDHE_PSK_WITH_RC4_128_SHA", "TLS_ECDHE_RSA_WITH_RC4_128_SHA", "TLS_ECDH_RSA_WITH_RC4_128_SHA", "TLS_KRB5_EXPORT_WITH_RC4_40_MD5", "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", "TLS_KRB5_WITH_RC4_128_MD5", "TLS_KRB5_WITH_RC4_128_SHA", "TLS_PSK_WITH_RC4_128_SHA", "TLS_RSA_PSK_WITH_RC4_128_SHA", }; /** * @throws IllegalArgumentException if the value could not be parsed */ public static String[] getBannedCipherSuitesFromConfigurationValue(String value) { List<String> values = Lists.newArrayList(CIPHER_FLAG_SPLITTER.split(value)); CipherFlag[] flags = new CipherFlag[values.size()]; for (int i = 0; i < values.size(); i++) { flags[i] = CipherFlag.valueOf(values.get(i)); } return getBannedCipherSuites(flags); } public static String[] getBannedCipherSuites(CipherFlag[] flags) { Set<String> banned = new HashSet<>(); for (CipherFlag flag : flags) { switch (flag) { case DISABLE_EC: banned.addAll(asList(EC_CIPHERS)); break; case DISABLE_RC4: banned.addAll(asList(RC4_CIPHERS)); break; } } return banned.toArray(new String[banned.size()]); } }