diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java
index fd1876558..5c8d93981 100644
--- a/src/com/fsck/k9/mail/store/ImapStore.java
+++ b/src/com/fsck/k9/mail/store/ImapStore.java
@@ -2449,7 +2449,7 @@ public class ImapStore extends Store {
sslContext.init(null, new TrustManager[] {
TrustManagerFactory.get(mSettings.getHost(), secure)
}, new SecureRandom());
- mSocket = sslContext.getSocketFactory().createSocket();
+ mSocket = TrustedSocketFactory.createSocket(sslContext);
} else {
mSocket = new Socket();
}
diff --git a/src/com/fsck/k9/mail/store/Pop3Store.java b/src/com/fsck/k9/mail/store/Pop3Store.java
index 7cac7b48e..1621c1527 100644
--- a/src/com/fsck/k9/mail/store/Pop3Store.java
+++ b/src/com/fsck/k9/mail/store/Pop3Store.java
@@ -330,7 +330,7 @@ public class Pop3Store extends Store {
sslContext.init(null, new TrustManager[] {
TrustManagerFactory.get(mHost, secure)
}, new SecureRandom());
- mSocket = sslContext.getSocketFactory().createSocket();
+ mSocket = TrustedSocketFactory.createSocket(sslContext);
} else {
mSocket = new Socket();
}
diff --git a/src/com/fsck/k9/mail/store/TrustedSocketFactory.java b/src/com/fsck/k9/mail/store/TrustedSocketFactory.java
new file mode 100644
index 000000000..5268c01ee
--- /dev/null
+++ b/src/com/fsck/k9/mail/store/TrustedSocketFactory.java
@@ -0,0 +1,96 @@
+package com.fsck.k9.mail.store;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import java.io.IOException;
+import java.net.Socket;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.*;
+
+
+/**
+ * Filter and reorder list of cipher suites and TLS versions.
+ *
+ *
+ * See: http://op-co.de/blog/posts/android_ssl_downgrade/
+ *
+ */
+public class TrustedSocketFactory {
+ protected static final String ENABLED_CIPHERS[];
+ protected static final String ENABLED_PROTOCOLS[];
+
+ static {
+ String preferredCiphers[] = {
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
+ "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
+ "TLS_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_RSA_WITH_AES_256_CBC_SHA",
+ "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_RSA_WITH_RC4_128_SHA",
+ "SSL_RSA_WITH_RC4_128_MD5",
+ };
+ String preferredProtocols[] = {
+ "TLSv1.2", "TLSv1.1", "TLSv1"
+ };
+
+ String[] supportedCiphers = null;
+ String[] supportedProtocols = null;
+
+ try {
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, null, new SecureRandom());
+ SSLSocketFactory sf = sslContext.getSocketFactory();
+ supportedCiphers = sf.getSupportedCipherSuites();
+ SSLSocket sock = (SSLSocket)sf.createSocket();
+ supportedProtocols = sock.getSupportedProtocols();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ } catch (KeyManagementException kme) {
+ kme.printStackTrace();
+ } catch (NoSuchAlgorithmException nsae) {
+ nsae.printStackTrace();
+ }
+
+ ENABLED_CIPHERS = supportedCiphers == null ? null :
+ filterBySupport(preferredCiphers, supportedCiphers);
+ ENABLED_PROTOCOLS = supportedProtocols == null ? null :
+ filterBySupport(preferredProtocols, supportedProtocols);
+ }
+
+ protected static String[] filterBySupport(String[] preferred, String[] supported) {
+ List enabled = new ArrayList();
+ Set available = new HashSet();
+ Collections.addAll(available, supported);
+
+ for (String item : preferred) {
+ if (available.contains(item)) enabled.add(item);
+ }
+ return enabled.toArray(new String[enabled.size()]);
+ }
+
+ public static Socket createSocket(SSLContext sslContext) throws IOException {
+ SSLSocket socket = (SSLSocket) sslContext.getSocketFactory().createSocket();
+ hardenSocket(socket);
+
+ return socket;
+ }
+
+ private static void hardenSocket(SSLSocket sock) {
+ if (ENABLED_CIPHERS != null) {
+ sock.setEnabledCipherSuites(ENABLED_CIPHERS);
+ }
+ if (ENABLED_PROTOCOLS != null) {
+ sock.setEnabledProtocols(ENABLED_PROTOCOLS);
+ }
+ }
+}
diff --git a/src/com/fsck/k9/mail/transport/TrustedSocketFactory.java b/src/com/fsck/k9/mail/store/WebDavSocketFactory.java
similarity index 64%
rename from src/com/fsck/k9/mail/transport/TrustedSocketFactory.java
rename to src/com/fsck/k9/mail/store/WebDavSocketFactory.java
index fa23f079b..2d4f959ed 100644
--- a/src/com/fsck/k9/mail/transport/TrustedSocketFactory.java
+++ b/src/com/fsck/k9/mail/store/WebDavSocketFactory.java
@@ -1,14 +1,9 @@
-package com.fsck.k9.mail.transport;
+package com.fsck.k9.mail.store;
-import com.fsck.k9.mail.store.TrustManagerFactory;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.scheme.LayeredSocketFactory;
import org.apache.http.params.HttpParams;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
@@ -17,24 +12,34 @@ import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
-public class TrustedSocketFactory implements LayeredSocketFactory {
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+
+/*
+ * TODO: find out what's going on here and document it.
+ * Using two socket factories looks suspicious.
+ */
+public class WebDavSocketFactory implements LayeredSocketFactory {
private SSLSocketFactory mSocketFactory;
private org.apache.http.conn.ssl.SSLSocketFactory mSchemeSocketFactory;
- public TrustedSocketFactory(String host, boolean secure) throws NoSuchAlgorithmException, KeyManagementException {
+ public WebDavSocketFactory(String host, boolean secure) throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] {
- TrustManagerFactory.get(host, secure)
- }, new SecureRandom());
+ TrustManagerFactory.get(host, secure)
+ }, new SecureRandom());
mSocketFactory = sslContext.getSocketFactory();
mSchemeSocketFactory = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
mSchemeSocketFactory.setHostnameVerifier(
- org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+ org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
}
public Socket connectSocket(Socket sock, String host, int port,
- InetAddress localAddress, int localPort, HttpParams params)
- throws IOException, UnknownHostException, ConnectTimeoutException {
+ InetAddress localAddress, int localPort, HttpParams params)
+ throws IOException, UnknownHostException, ConnectTimeoutException {
return mSchemeSocketFactory.connectSocket(sock, host, port, localAddress, localPort, params);
}
@@ -46,17 +51,17 @@ public class TrustedSocketFactory implements LayeredSocketFactory {
return mSchemeSocketFactory.isSecure(sock);
}
public Socket createSocket(
- final Socket socket,
- final String host,
- final int port,
- final boolean autoClose
+ final Socket socket,
+ final String host,
+ final int port,
+ final boolean autoClose
) throws IOException, UnknownHostException {
SSLSocket sslSocket = (SSLSocket) mSocketFactory.createSocket(
- socket,
- host,
- port,
- autoClose
- );
+ socket,
+ host,
+ port,
+ autoClose
+ );
//hostnameVerifier.verify(host, sslSocket);
// verifyHostName() didn't blowup - good!
return sslSocket;
diff --git a/src/com/fsck/k9/mail/store/WebDavStore.java b/src/com/fsck/k9/mail/store/WebDavStore.java
index 6d4b69d3f..b3f8d5f24 100644
--- a/src/com/fsck/k9/mail/store/WebDavStore.java
+++ b/src/com/fsck/k9/mail/store/WebDavStore.java
@@ -10,7 +10,6 @@ import com.fsck.k9.mail.*;
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
import com.fsck.k9.mail.internet.MimeMessage;
-import com.fsck.k9.mail.transport.TrustedSocketFactory;
import org.apache.commons.io.IOUtils;
import org.apache.http.*;
import org.apache.http.client.CookieStore;
@@ -1080,7 +1079,7 @@ public class WebDavStore extends Store {
SchemeRegistry reg = mHttpClient.getConnectionManager().getSchemeRegistry();
try {
- Scheme s = new Scheme("https", new TrustedSocketFactory(mHost, mSecure), 443);
+ Scheme s = new Scheme("https", new WebDavSocketFactory(mHost, mSecure), 443);
reg.register(s);
} catch (NoSuchAlgorithmException nsa) {
Log.e(K9.LOG_TAG, "NoSuchAlgorithmException in getHttpClient: " + nsa);
diff --git a/src/com/fsck/k9/mail/transport/SmtpTransport.java b/src/com/fsck/k9/mail/transport/SmtpTransport.java
index 30ad91b4d..4e71d3ee8 100644
--- a/src/com/fsck/k9/mail/transport/SmtpTransport.java
+++ b/src/com/fsck/k9/mail/transport/SmtpTransport.java
@@ -14,6 +14,7 @@ import com.fsck.k9.mail.filter.SmtpDataStuffing;
import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.store.TrustManagerFactory;
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
+import com.fsck.k9.mail.store.TrustedSocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
@@ -244,7 +245,7 @@ public class SmtpTransport extends Transport {
sslContext.init(null, new TrustManager[] {
TrustManagerFactory.get(mHost, secure)
}, new SecureRandom());
- mSocket = sslContext.getSocketFactory().createSocket();
+ mSocket = TrustedSocketFactory.createSocket(sslContext);
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
} else {
mSocket = new Socket();