From c0453065e4b14fb307556dababcfbdc743fce0b8 Mon Sep 17 00:00:00 2001 From: Markus Richter <8398165+mqus@users.noreply.github.com> Date: Sun, 8 Dec 2019 17:09:16 +0100 Subject: [PATCH 1/4] Enable TLS v1.1/1.2 for KitKat devices This enables modern TLS versions in the collection browser, the Downloader and the Player. This is neccessary because media.ccc.de rejects all older TLS connection attempts, see issue #2777. --- .../org/schabi/newpipe/DownloaderImpl.java | 47 ++++++- .../java/org/schabi/newpipe/MainActivity.java | 5 + .../newpipe/util/TLSSocketFactoryCompat.java | 127 ++++++++++++++++++ 3 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java index 5dd9d1b74..8704bedb0 100644 --- a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java +++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java @@ -1,20 +1,32 @@ package org.schabi.newpipe; +import android.os.Build; import android.text.TextUtils; import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Request; import org.schabi.newpipe.extractor.downloader.Response; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; +import org.schabi.newpipe.util.TLSSocketFactoryCompat; import java.io.IOException; import java.io.InputStream; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import androidx.annotation.NonNull; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import okhttp3.OkHttpClient; @@ -29,6 +41,9 @@ public class DownloaderImpl extends Downloader { private OkHttpClient client; private DownloaderImpl(OkHttpClient.Builder builder) { + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + enableModernTLS(builder); + } this.client = builder .readTimeout(30, TimeUnit.SECONDS) //.cache(new Cache(new File(context.getExternalCacheDir(), "okhttp"), 16 * 1024 * 1024)) @@ -154,4 +169,34 @@ public class DownloaderImpl extends Downloader { return new Response(response.code(), response.message(), response.headers().toMultimap(), responseBodyToReturn); } + + /** + * Enable TLS 1.2 and 1.1 on Android Kitkat. This function is mostly taken from the documentation of + * OkHttpClient.Builder.sslSocketFactory(_,_) + * + * If there is an error, It will safely fall back to doing nothing and printing the Error to the console. + * @param builder The HTTPClient Builder on which TLS is enabled on (will be modified in-place) + */ + private static void enableModernTLS(OkHttpClient.Builder builder) { + try { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore) null); + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + throw new IllegalStateException("Unexpected default trust managers:" + + Arrays.toString(trustManagers)); + } + X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[] { trustManager }, null); + //SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + SSLSocketFactory sslSocketFactory = TLSSocketFactoryCompat.getInstance(); + + builder.sslSocketFactory(sslSocketFactory, trustManager); + } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { + e.printStackTrace(); + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 927fc1589..2da01991c 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -72,6 +72,7 @@ import org.schabi.newpipe.util.PeertubeHelper; import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.StateSaver; +import org.schabi.newpipe.util.TLSSocketFactoryCompat; import org.schabi.newpipe.util.ThemeHelper; import java.util.ArrayList; @@ -108,6 +109,10 @@ public class MainActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]"); + //enable TLS1.1/1.2 for kitkat devices, to fix download and play for mediaCCC sources + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) + TLSSocketFactoryCompat.setAsDefault(); + ThemeHelper.setTheme(this, ServiceHelper.getSelectedServiceId(this)); super.onCreate(savedInstanceState); diff --git a/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java b/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java new file mode 100644 index 000000000..67d8273d2 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java @@ -0,0 +1,127 @@ +package org.schabi.newpipe.util; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; + + +/** + * This is an extension of the SSLSocketFactory which enables TLS 1.2 and 1.1. + * Created for usage on Android 4.1-4.4 devices, which haven't enabled those by default. + */ +public class TLSSocketFactoryCompat extends SSLSocketFactory { + + + private static TLSSocketFactoryCompat instance=null; + + private SSLSocketFactory internalSSLSocketFactory; + + public static TLSSocketFactoryCompat getInstance() throws NoSuchAlgorithmException, KeyManagementException { + if(instance!=null) + return instance; + return instance=new TLSSocketFactoryCompat(); + } + + + public TLSSocketFactoryCompat() throws KeyManagementException, NoSuchAlgorithmException { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, null, null); + internalSSLSocketFactory = context.getSocketFactory(); + } + + public TLSSocketFactoryCompat(TrustManager[] tm) throws KeyManagementException, NoSuchAlgorithmException { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, tm, new java.security.SecureRandom()); + internalSSLSocketFactory = context.getSocketFactory(); + } + + public static void setAsDefault() { + try { + HttpsURLConnection.setDefaultSSLSocketFactory(getInstance()); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + e.printStackTrace(); + } + } + + @Override + public String[] getDefaultCipherSuites() { + return internalSSLSocketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return internalSSLSocketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket() throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket()); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort)); + } + + private Socket enableTLSOnSocket(Socket socket) { + if(socket != null && (socket instanceof SSLSocket)) { + /* + //Create list of supported protocols + ArrayList supportedProtocols = new ArrayList<>(); + for (String protocol : ((SSLSocket)socket).getEnabledProtocols()) { + + //Log.d("TLSSocketFactory", "Supported protocol:" + protocol); + //Only add TLS protocols (don't want ot support older SSL versions) + if (protocol.toUpperCase().contains("TLS")) { + supportedProtocols.add(protocol); + } + } + //Force add TLSv1.1 and 1.2 if not already added + if (!supportedProtocols.contains("TLSv1.1")) { + supportedProtocols.add("TLSv1.1"); + } + if (!supportedProtocols.contains("TLSv1.2")) { + supportedProtocols.add("TLSv1.2"); + } + + String[] protocolArray = supportedProtocols.toArray(new String[supportedProtocols.size()]); + + //enable protocols in our list + //((SSLSocket)socket).setEnabledProtocols(protocolArray); + */ + // OR: only enable TLS 1.1 and 1.2! + ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"}); + + } + return socket; + } +} \ No newline at end of file From 3e409b9cc1183eab1ff5a03c024e54b4a92cf1ec Mon Sep 17 00:00:00 2001 From: Markus <8398165+mqus@users.noreply.github.com> Date: Wed, 4 Dec 2019 12:49:47 +0100 Subject: [PATCH 2/4] Fix formatting and remove unused code --- .../main/java/org/schabi/newpipe/DownloaderImpl.java | 8 ++++---- .../main/java/org/schabi/newpipe/MainActivity.java | 5 +++-- .../schabi/newpipe/util/TLSSocketFactoryCompat.java | 11 ++++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java index 8704bedb0..dc8c6f135 100644 --- a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java +++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java @@ -173,12 +173,14 @@ public class DownloaderImpl extends Downloader { /** * Enable TLS 1.2 and 1.1 on Android Kitkat. This function is mostly taken from the documentation of * OkHttpClient.Builder.sslSocketFactory(_,_) + *

+ * If there is an error, the function will safely fall back to doing nothing and printing the error to the console. * - * If there is an error, It will safely fall back to doing nothing and printing the Error to the console. * @param builder The HTTPClient Builder on which TLS is enabled on (will be modified in-place) */ private static void enableModernTLS(OkHttpClient.Builder builder) { try { + // get the default TrustManager TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init((KeyStore) null); @@ -189,9 +191,7 @@ public class DownloaderImpl extends Downloader { } X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, new TrustManager[] { trustManager }, null); - //SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + // insert our own TLSSocketFactory SSLSocketFactory sslSocketFactory = TLSSocketFactoryCompat.getInstance(); builder.sslSocketFactory(sslSocketFactory, trustManager); diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 2da01991c..90d299c7f 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -109,9 +109,10 @@ public class MainActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]"); - //enable TLS1.1/1.2 for kitkat devices, to fix download and play for mediaCCC sources - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) + // enable TLS1.1/1.2 for kitkat devices, to fix download and play for mediaCCC sources + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { TLSSocketFactoryCompat.setAsDefault(); + } ThemeHelper.setTheme(this, ServiceHelper.getSelectedServiceId(this)); diff --git a/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java b/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java index 67d8273d2..2f6a650ff 100644 --- a/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java +++ b/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java @@ -21,14 +21,15 @@ import javax.net.ssl.TrustManager; public class TLSSocketFactoryCompat extends SSLSocketFactory { - private static TLSSocketFactoryCompat instance=null; + private static TLSSocketFactoryCompat instance = null; private SSLSocketFactory internalSSLSocketFactory; public static TLSSocketFactoryCompat getInstance() throws NoSuchAlgorithmException, KeyManagementException { - if(instance!=null) + if (instance != null) { return instance; - return instance=new TLSSocketFactoryCompat(); + } + return instance = new TLSSocketFactoryCompat(); } @@ -93,7 +94,7 @@ public class TLSSocketFactoryCompat extends SSLSocketFactory { } private Socket enableTLSOnSocket(Socket socket) { - if(socket != null && (socket instanceof SSLSocket)) { + if (socket != null && (socket instanceof SSLSocket)) { /* //Create list of supported protocols ArrayList supportedProtocols = new ArrayList<>(); @@ -119,7 +120,7 @@ public class TLSSocketFactoryCompat extends SSLSocketFactory { //((SSLSocket)socket).setEnabledProtocols(protocolArray); */ // OR: only enable TLS 1.1 and 1.2! - ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"}); + ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.1", "TLSv1.2"}); } return socket; From 23c2f748d6c80cb5d745a01c55acef311451f35f Mon Sep 17 00:00:00 2001 From: Markus <8398165+mqus@users.noreply.github.com> Date: Wed, 11 Dec 2019 14:58:53 +0100 Subject: [PATCH 3/4] Add trying out some more cipher suites which may be supported on non-standard Android 4.4.2 devices --- .../java/org/schabi/newpipe/DownloaderImpl.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java index dc8c6f135..cceb662f7 100644 --- a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java +++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java @@ -15,12 +15,12 @@ import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; @@ -29,6 +29,8 @@ import javax.net.ssl.X509TrustManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import okhttp3.CipherSuite; +import okhttp3.ConnectionSpec; import okhttp3.OkHttpClient; import okhttp3.RequestBody; import okhttp3.ResponseBody; @@ -195,6 +197,19 @@ public class DownloaderImpl extends Downloader { SSLSocketFactory sslSocketFactory = TLSSocketFactoryCompat.getInstance(); builder.sslSocketFactory(sslSocketFactory, trustManager); + + // This will try to enable all modern CipherSuites(+2 more) that are supported on the device. + // Necessary because some servers (e.g. Framatube.org) don't support the old cipher suites. + // https://github.com/square/okhttp/issues/4053#issuecomment-402579554 + List cipherSuites = new ArrayList<>(); + cipherSuites.addAll(ConnectionSpec.MODERN_TLS.cipherSuites()); + cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA); + cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA); + ConnectionSpec legacyTLS = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .cipherSuites(cipherSuites.toArray(new CipherSuite[0])) + .build(); + + builder.connectionSpecs(Arrays.asList(legacyTLS, ConnectionSpec.CLEARTEXT)); } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { e.printStackTrace(); } From 559bcfc6a5b2bb3e1c2f79954a909eb3bf63d972 Mon Sep 17 00:00:00 2001 From: Markus <8398165+mqus@users.noreply.github.com> Date: Fri, 13 Dec 2019 21:15:16 +0100 Subject: [PATCH 4/4] Remove commented-out code and hide stacktraces in release mode --- .../org/schabi/newpipe/DownloaderImpl.java | 4 ++- .../newpipe/util/TLSSocketFactoryCompat.java | 30 ++----------------- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java index cceb662f7..7e4ac304e 100644 --- a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java +++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java @@ -35,6 +35,8 @@ import okhttp3.OkHttpClient; import okhttp3.RequestBody; import okhttp3.ResponseBody; +import static org.schabi.newpipe.MainActivity.DEBUG; + public class DownloaderImpl extends Downloader { public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0"; @@ -211,7 +213,7 @@ public class DownloaderImpl extends Downloader { builder.connectionSpecs(Arrays.asList(legacyTLS, ConnectionSpec.CLEARTEXT)); } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { - e.printStackTrace(); + if (DEBUG) e.printStackTrace(); } } } diff --git a/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java b/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java index 2f6a650ff..d8b6f78f5 100644 --- a/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java +++ b/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java @@ -13,6 +13,8 @@ import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; +import static org.schabi.newpipe.MainActivity.DEBUG; + /** * This is an extension of the SSLSocketFactory which enables TLS 1.2 and 1.1. @@ -49,7 +51,7 @@ public class TLSSocketFactoryCompat extends SSLSocketFactory { try { HttpsURLConnection.setDefaultSSLSocketFactory(getInstance()); } catch (NoSuchAlgorithmException | KeyManagementException e) { - e.printStackTrace(); + if (DEBUG) e.printStackTrace(); } } @@ -95,33 +97,7 @@ public class TLSSocketFactoryCompat extends SSLSocketFactory { private Socket enableTLSOnSocket(Socket socket) { if (socket != null && (socket instanceof SSLSocket)) { - /* - //Create list of supported protocols - ArrayList supportedProtocols = new ArrayList<>(); - for (String protocol : ((SSLSocket)socket).getEnabledProtocols()) { - - //Log.d("TLSSocketFactory", "Supported protocol:" + protocol); - //Only add TLS protocols (don't want ot support older SSL versions) - if (protocol.toUpperCase().contains("TLS")) { - supportedProtocols.add(protocol); - } - } - //Force add TLSv1.1 and 1.2 if not already added - if (!supportedProtocols.contains("TLSv1.1")) { - supportedProtocols.add("TLSv1.1"); - } - if (!supportedProtocols.contains("TLSv1.2")) { - supportedProtocols.add("TLSv1.2"); - } - - String[] protocolArray = supportedProtocols.toArray(new String[supportedProtocols.size()]); - - //enable protocols in our list - //((SSLSocket)socket).setEnabledProtocols(protocolArray); - */ - // OR: only enable TLS 1.1 and 1.2! ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.1", "TLSv1.2"}); - } return socket; }