Don't reorder TLS ciphers on newer Android versions
@@ -11,6 +11,7 @@ import java.util.List;
import android.content.Context;
import android.net.SSLCertificateSocketFactory;
+import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
@@ -25,7 +26,11 @@ import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
/**
- * Filter and reorder list of cipher suites and TLS versions.
+ * Prior to API 21 (and notably from API 10 - 2.3.4) Android weakened it's cipher list
+ * by ordering them badly such that RC4-MD5 was preferred. To work around this we
+ * remove the insecure ciphers and reorder them so the latest more secure ciphers are at the top.
+ *
+ * On more modern versions of Android we keep the system configuration.
*/
public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
protected static final String[] ENABLED_CIPHERS;
@@ -75,6 +80,11 @@ public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
"TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
"SSL_RSA_WITH_RC4_128_SHA",
"SSL_RSA_WITH_RC4_128_MD5",
+ "TLS_ECDH_RSA_WITH_NULL_SHA",
+ "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDH_anon_WITH_NULL_SHA",
+ "TLS_ECDH_anon_WITH_RC4_128_SHA",
+ "TLS_RSA_WITH_NULL_SHA256"
};
protected static final String[] ORDERED_KNOWN_PROTOCOLS = {
@@ -107,17 +117,28 @@ public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
"protocols", e);
}
+ if (hasWeakSslImplementation()) {
ENABLED_CIPHERS = (enabledCiphers == null) ? null :
reorder(enabledCiphers, ORDERED_KNOWN_CIPHERS, BLACKLISTED_CIPHERS);
-
ENABLED_PROTOCOLS = (supportedProtocols == null) ? null :
reorder(supportedProtocols, ORDERED_KNOWN_PROTOCOLS, BLACKLISTED_PROTOCOLS);
+ } else {
+ ENABLED_CIPHERS = (enabledCiphers == null) ? null :
+ remove(enabledCiphers, BLACKLISTED_CIPHERS);
+ ENABLED_PROTOCOLS = (supportedProtocols == null) ? null :
+ remove(supportedProtocols, BLACKLISTED_PROTOCOLS);
+ }
+
}
public DefaultTrustedSocketFactory(Context context) {
this.context = context;
}
+ private static boolean hasWeakSslImplementation() {
+ return android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP;
+ }
+
protected static String[] reorder(String[] enabled, String[] known, String[] blacklisted) {
List<String> unknown = new ArrayList<String>();
Collections.addAll(unknown, enabled);
@@ -144,6 +165,20 @@ public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
return result.toArray(new String[result.size()]);
}
+ protected static String[] remove(String[] enabled, String[] blacklisted) {
+ List<String> items = new ArrayList<String>();
+ Collections.addAll(items, enabled);
+
+ // Remove blacklisted items
+ if (blacklisted != null) {
+ for (String item : blacklisted) {
+ items.remove(item);
+ }
+ }
+
+ return items.toArray(new String[items.size()]);
+ }
+
private Context context;
public Socket createSocket(Socket socket, String host, int port, String clientCertificateAlias)
@@ -166,7 +201,9 @@ public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
}
SSLSocket sslSocket = (SSLSocket) trustedSocket;
+
hardenSocket(sslSocket);
+
setSniHost(socketFactory, sslSocket, host);
return trustedSocket;
